Merge branch 'features/r2_11_2_alphafold/JAL-629' into features/JAL-3858_PAEsInProjects
[jalview.git] / src / jalview / gui / StructureChooser.java
index ce57373..9f259bb 100644 (file)
@@ -24,18 +24,19 @@ package jalview.gui;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.Callable;
 import java.util.concurrent.Executors;
 
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
-import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JPopupMenu;
 import javax.swing.JTable;
@@ -44,6 +45,7 @@ import javax.swing.table.AbstractTableModel;
 
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.bin.Jalview;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
@@ -64,6 +66,7 @@ import jalview.jbgui.GStructureChooser;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.DBRefFetcher;
 import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
 import jalview.ws.seqfetcher.DbSourceProxy;
@@ -81,6 +84,11 @@ public class StructureChooser extends GStructureChooser
 {
   private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
 
+  /**
+   * warn user if need to fetch more than this many uniprot records at once
+   */
+  private static final int THRESHOLD_WARN_UNIPROT_FETCH_NEEDED = 20;
+
   private SequenceI selectedSequence;
 
   private SequenceI[] selectedSequences;
@@ -111,11 +119,19 @@ public class StructureChooser extends GStructureChooser
 
   List<SequenceI> seqsWithoutSourceDBRef = null;
 
+  private boolean noChooserGUI = false;
+
   private static StructureViewer lastTargetedView = null;
 
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
   {
+    this(selectedSeqs, selectedSeq, ap, false);
+  }
+
+  public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
+          AlignmentPanel ap, boolean noChooserGUI)
+  {
     // which FTS engine to use
     data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
     initDialog();
@@ -124,6 +140,7 @@ public class StructureChooser extends GStructureChooser
     this.selectedSequence = selectedSeq;
     this.selectedSequences = selectedSeqs;
     this.progressIndicator = (ap == null) ? null : ap.alignFrame;
+    this.noChooserGUI = noChooserGUI;
     init();
 
   }
@@ -197,6 +214,7 @@ public class StructureChooser extends GStructureChooser
 
     Executors.defaultThreadFactory().newThread(new Runnable()
     {
+      @Override
       public void run()
       {
         populateSeqsWithoutSourceDBRef();
@@ -238,13 +256,15 @@ public class StructureChooser extends GStructureChooser
     populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
     discoverStructureViews();
     updateProgressIndicator(null, startTime);
-    mainFrame.setVisible(true);
+    mainFrame.setVisible(noChooserGUI);
     updateCurrentView();
   }
 
   /**
    * raises dialog for Uniprot fetch followed by 3D beacons search
-   * @param ignoreGui - when true, don't ask, just fetch 
+   * 
+   * @param ignoreGui
+   *          - when true, don't ask, just fetch
    */
   public void promptForTDBFetch(boolean ignoreGui)
   {
@@ -258,24 +278,34 @@ public class StructureChooser extends GStructureChooser
       {
         mainFrame.setEnabled(false);
         cmb_filterOption.setEnabled(false);
-        progressBar.setProgressBar(MessageManager.getString("status.searching_3d_beacons"), progressId);
+        progressBar.setProgressBar(
+                MessageManager.getString("status.searching_3d_beacons"),
+                progressId);
+        btn_queryTDB.setEnabled(false);
         // TODO: warn if no accessions discovered
         populateSeqsWithoutSourceDBRef();
         // redo initial discovery - this time with 3d beacons
         // Executors.
-        previousWantedFields=null;
-        lastSelected=(FilterOption) cmb_filterOption.getSelectedItem();
+        previousWantedFields = null;
+        lastSelected = (FilterOption) cmb_filterOption.getSelectedItem();
         cmb_filterOption.setSelectedItem(null);
-        cachedPDBExists=false; // reset to initial
+        cachedPDBExists = false; // reset to initial
         initialStructureDiscovery();
         if (!isStructuresDiscovered())
         {
-          progressBar.setProgressBar(MessageManager.getString("status.no_structures_discovered_from_3d_beacons"), progressId);
-          btn_queryTDB.setToolTipText(MessageManager.getString("status.no_structures_discovered_from_3d_beacons"));
+          progressBar.setProgressBar(MessageManager.getString(
+                  "status.no_structures_discovered_from_3d_beacons"),
+                  progressId);
+          btn_queryTDB.setToolTipText(MessageManager.getString(
+                  "status.no_structures_discovered_from_3d_beacons"));
           btn_queryTDB.setEnabled(false);
-        } else {
+          pnl_queryTDB.setVisible(false);
+        }
+        else
+        {
           cmb_filterOption.setSelectedIndex(0); // select 'best'
           btn_queryTDB.setVisible(false);
+          pnl_queryTDB.setVisible(false);
           progressBar.setProgressBar(null, progressId);
         }
         mainFrame.setEnabled(true);
@@ -285,7 +315,7 @@ public class StructureChooser extends GStructureChooser
 
     final FetchFinishedListenerI afterDbRefFetch = new FetchFinishedListenerI()
     {
-      
+
       @Override
       public void finished()
       {
@@ -293,51 +323,55 @@ public class StructureChooser extends GStructureChooser
         notQueriedTDBYet = false;
         // new thread to discover structures - via 3d beacons
         Executors.defaultThreadFactory().newThread(strucDiscovery).start();
-        
+
       }
     };
-    
+
     // fetch db refs if OK pressed
-    final Runnable discoverCanonicalDBrefs = new Runnable() 
-    {
-      @Override
-      public void run()
+    final Callable discoverCanonicalDBrefs = () -> {
+      btn_queryTDB.setEnabled(false);
+      populateSeqsWithoutSourceDBRef();
+
+      final int y = seqsWithoutSourceDBRef.size();
+      if (y > 0)
       {
-        populateSeqsWithoutSourceDBRef();
+        final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                .toArray(new SequenceI[y]);
+        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
+                progressBar, new DbSourceProxy[]
+                { new jalview.ws.dbsources.Uniprot() }, null, false);
+        dbRefFetcher.addListener(afterDbRefFetch);
+        // ideally this would also gracefully run with callbacks
 
-        final int y = seqsWithoutSourceDBRef.size();
-        if (y > 0)
-        {
-          final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
-                  .toArray(new SequenceI[y]);
-          DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
-                  progressBar, new DbSourceProxy[]
-                  { new jalview.ws.dbsources.Uniprot() }, null, false);
-          dbRefFetcher.addListener(afterDbRefFetch);
-          // ideally this would also gracefully run with callbacks
-          dbRefFetcher.fetchDBRefs(true);
-        } else {
-          // call finished action directly
-          afterDbRefFetch.finished();
-        }
+        dbRefFetcher.fetchDBRefs(true);
       }
-
+      else
+      {
+        // call finished action directly
+        afterDbRefFetch.finished();
+      }
+      return null;
     };
-    final Runnable revertview = new Runnable() {
-      public void run() {
-        if (lastSelected!=null) {
-          cmb_filterOption.setSelectedItem(lastSelected);
-        }
-      };
+    final Callable revertview = () -> {
+      if (lastSelected != null)
+      {
+        cmb_filterOption.setSelectedItem(lastSelected);
+      }
+      return null;
     };
-    if (ignoreGui)
+    int threshold = Cache.getDefault("UNIPROT_AUTOFETCH_THRESHOLD",
+            THRESHOLD_WARN_UNIPROT_FETCH_NEEDED);
+    Console.debug("Using Uniprot fetch threshold of " + threshold);
+    if (ignoreGui || seqsWithoutSourceDBRef.size() < threshold)
     {
-      Executors.defaultThreadFactory().newThread(discoverCanonicalDBrefs).start();
+      Executors.newSingleThreadExecutor().submit(discoverCanonicalDBrefs);
       return;
     }
     // need cancel and no to result in the discoverPDB action - mocked is
     // 'cancel' TODO: mock should be OK
-    JvOptionPane.newOptionDialog(this)
+
+    StructureChooser thisSC = this;
+    JvOptionPane.newOptionDialog(thisSC.getFrame())
             .setResponseHandler(JvOptionPane.OK_OPTION,
                     discoverCanonicalDBrefs)
             .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
@@ -346,13 +380,12 @@ public class StructureChooser extends GStructureChooser
                     MessageManager.formatMessage(
                             "label.fetch_references_for_3dbeacons",
                             seqsWithoutSourceDBRef.size()),
-                    MessageManager
-                            .getString("label.3dbeacons"),
+                    MessageManager.getString("label.3dbeacons"),
                     JvOptionPane.YES_NO_OPTION, JvOptionPane.PLAIN_MESSAGE,
                     null, new Object[]
                     { MessageManager.getString("action.ok"),
                         MessageManager.getString("action.cancel") },
-                    MessageManager.getString("action.ok"));
+                    MessageManager.getString("action.ok"), false);
   }
 
   /**
@@ -624,7 +657,7 @@ public class StructureChooser extends GStructureChooser
     // StructureChooser
     // works
     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
+            Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new jalview.io.JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.formatMessage("label.select_pdb_file_for",
@@ -637,7 +670,7 @@ public class StructureChooser extends GStructureChooser
     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
     {
       selectedPdbFileName = chooser.getSelectedFile().getPath();
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
+      Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
       validateSelections();
     }
   }
@@ -693,6 +726,7 @@ public class StructureChooser extends GStructureChooser
     if (canQueryTDB && notQueriedTDBYet)
     {
       btn_queryTDB.setVisible(true);
+      pnl_queryTDB.setVisible(true);
     }
 
     if (cachedPDBExist)
@@ -720,7 +754,7 @@ public class StructureChooser extends GStructureChooser
   {
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
             .getSelectedItem());
-    
+
     if (lastSelected == selectedFilterOpt)
     {
       // don't need to do anything, probably
@@ -735,7 +769,7 @@ public class StructureChooser extends GStructureChooser
     String filterTitle = mainFrame.getTitle();
     mainFrame.setTitle(frameTitle);
     chk_invertFilter.setVisible(false);
-    
+
     if (selectedFilterOpt.getView() == VIEWS_FILTER)
     {
       mainFrame.setTitle(filterTitle);
@@ -826,32 +860,40 @@ public class StructureChooser extends GStructureChooser
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
             .getSelectedItem());
     String currentView = selectedFilterOpt.getView();
-     
-    if (currentView == VIEWS_FILTER && data instanceof ThreeDBStructureChooserQuerySource)
+
+    if (currentView == VIEWS_FILTER
+            && data instanceof ThreeDBStructureChooserQuerySource)
     {
-      
-      TDB_FTSData row=((ThreeDBStructureChooserQuerySource)data).getFTSDataFor(getResultTable(), selectedRow, discoveredStructuresSet);
-      String pageUrl = row.getModelViewUrl(); 
+
+      TDB_FTSData row = ((ThreeDBStructureChooserQuerySource) data)
+              .getFTSDataFor(getResultTable(), selectedRow,
+                      discoveredStructuresSet);
+      String pageUrl = row.getModelViewUrl();
       JPopupMenu popup = new JPopupMenu("3D Beacons");
       JMenuItem viewUrl = new JMenuItem("View model web page");
-      viewUrl.addActionListener(
-              new ActionListener() {
-                @Override
-                public void actionPerformed(ActionEvent e)
-                {
-                  Desktop.showUrl(pageUrl);
-                }
-              }
-              );
+      viewUrl.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          Desktop.showUrl(pageUrl);
+        }
+      });
       popup.add(viewUrl);
-      SwingUtilities.invokeLater(new Runnable()  {
-        public void run() { popup.show(getResultTable(), x, y); }
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          popup.show(getResultTable(), x, y);
+        }
       });
       return true;
     }
     // event not handled by us
     return false;
   }
+
   /**
    * Validates inputs from the Manual PDB entry panel
    */
@@ -923,7 +965,9 @@ public class StructureChooser extends GStructureChooser
   {
     validateSelections();
   }
-  private FilterOption lastSelected=null;
+
+  private FilterOption lastSelected = null;
+
   /**
    * Handles the state change event for the 'filter' combo-box and 'invert'
    * check-box
@@ -1080,7 +1124,8 @@ public class StructureChooser extends GStructureChooser
             if (pdbIdStr.split(":").length > 1)
             {
               pdbEntry.setId(pdbIdStr.split(":")[0]);
-              pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
+              pdbEntry.setChainCode(
+                      pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
             }
             else
             {
@@ -1348,6 +1393,7 @@ public class StructureChooser extends GStructureChooser
   {
     if (selectedSequences != null)
     {
+      lbl_loading.setVisible(true);
       Thread refreshThread = new Thread(new Runnable()
       {
         @Override
@@ -1359,6 +1405,7 @@ public class StructureChooser extends GStructureChooser
           filterResultSet(
                   ((FilterOption) cmb_filterOption.getSelectedItem())
                           .getValue());
+          lbl_loading.setVisible(false);
         }
       });
       refreshThread.start();
@@ -1479,7 +1526,8 @@ public class StructureChooser extends GStructureChooser
   @Override
   public void setProgressBar(String message, long id)
   {
-    progressBar.setProgressBar(message, id);
+    if (!Platform.isHeadless())
+      progressBar.setProgressBar(message, id);
   }
 
   @Override
@@ -1517,16 +1565,48 @@ public class StructureChooser extends GStructureChooser
             && mainFrame.isVisible()
             && cmb_filterOption.getSelectedItem() != null;
   }
+
   /**
    * 
    * @return true if the 3D-Beacons query button will/has been displayed
    */
-  public boolean isCanQueryTDB() {
-         return canQueryTDB;
+  public boolean isCanQueryTDB()
+  {
+    return canQueryTDB;
   }
 
   public boolean isNotQueriedTDBYet()
   {
     return notQueriedTDBYet;
   }
+
+  /**
+   * Open a single structure file for a given sequence
+   */
+  public static void openStructureFileForSequence(AlignmentPanel ap,
+          SequenceI seq, File sFile)
+  {
+    StructureChooser sc = new StructureChooser(new SequenceI[] { seq }, seq,
+            ap, true);
+    StructureSelectionManager ssm = ap.getStructureSelectionManager();
+    PDBEntry fileEntry = null;
+    try
+    {
+      fileEntry = new AssociatePdbFileWithSeq().associatePdbWithSeq(
+              sFile.getAbsolutePath(), DataSourceType.FILE, seq, true,
+              Desktop.instance);
+    } catch (Exception e)
+    {
+      Console.error("Could not open structure file '"
+              + sFile.getAbsolutePath() + "'");
+      return;
+    }
+
+    StructureViewer sViewer = sc.launchStructureViewer(ssm,
+            new PDBEntry[]
+            { fileEntry }, ap, new SequenceI[] { seq });
+
+    sc.mainFrame.dispose();
+    sc.noChooserGUI = false;
+  }
 }