JAL-3829 alternative UX: need to select 3d-beacons from dropdown if db-refs need...
authorJim Procter <j.procter@dundee.ac.uk>
Tue, 21 Sep 2021 10:36:27 +0000 (11:36 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Tue, 21 Sep 2021 10:36:27 +0000 (11:36 +0100)
resources/lang/Messages.properties
src/jalview/gui/StructureChooser.java
src/jalview/gui/structurechooser/StructureChooserQuerySource.java

index 92a6ab8..fabd577 100644 (file)
@@ -513,7 +513,10 @@ label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences =
 label.standard_databases = Standard Databases
 label.fetch_embl_uniprot = Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources
 label.fetch_uniprot_references = Fetch Uniprot references
+label.search_3dbeacons = Find models with 3D-Beacons
+label.3dbeacons = 3D-Beacons
 label.fetch_references_for = Fetch database references for {0} sequences ?
+label.fetch_references_for_3dbeacons = 3D Beacons needs Uniprot References. Fetch database references for {0} sequences ?
 label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults from user preferences.
 label.align_structures_using_linked_alignment_views = Superpose structures using {0} selected alignment view(s)
 label.threshold_feature_display_by_score = Threshold the feature display by score.
index 82ad03b..ae7971c 100644 (file)
@@ -73,6 +73,11 @@ public class StructureChooser extends GStructureChooser
 {
   private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
 
+  /**
+   * transient combo box choice for initiating 3db fetch
+   */
+  private static final String VIEWS_QUERYING_TDB = "QUERY_3DB";
+
   private SequenceI selectedSequence;
 
   private SequenceI[] selectedSequences;
@@ -97,25 +102,75 @@ public class StructureChooser extends GStructureChooser
 
   private Collection<FTSData> lastDiscoveredStructuresSet;
 
+  private boolean canQueryTDB = false;
+
+  private boolean notQueriedTDBYet = true;
+
+  List<SequenceI> seqsWithoutSourceDBRef = null;
+
   private static StructureViewer lastTargetedView = null;
 
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
   {
     // which FTS engine to use
-    data = StructureChooserQuerySource
-            .getQuerySourceFor(selectedSeqs);
+    data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
     initDialog();
-    
+
     this.ap = ap;
     this.selectedSequence = selectedSeq;
     this.selectedSequences = selectedSeqs;
     this.progressIndicator = (ap == null) ? null : ap.alignFrame;
     init();
-    
+
   }
 
   /**
+   * sets canQueryTDB if protein sequences without a canonical uniprot ref or at
+   * least one structure are discovered.
+   */
+  private void populateSeqsWithoutSourceDBRef()
+  {
+    seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
+    boolean needCanonical = false;
+    for (SequenceI seq : selectedSequences)
+    {
+      if (seq.isProtein())
+      {
+        int dbRef = ThreeDBStructureChooserQuerySource
+                .checkUniprotRefs(seq.getDBRefs());
+        if (dbRef < 0)
+        {
+          if (dbRef == -1)
+          {
+            // need to retrieve canonicals
+            needCanonical = true;
+            seqsWithoutSourceDBRef.add(seq);
+          }
+          else
+          {
+            // could be a sequence with pdb ref
+            if (seq.getAllPDBEntries() == null
+                    || seq.getAllPDBEntries().size() == 0)
+            {
+              seqsWithoutSourceDBRef.add(seq);
+            }
+          }
+        }
+      }
+    }
+    // retrieve database refs for protein sequences
+    if (!seqsWithoutSourceDBRef.isEmpty())
+    {
+      canQueryTDB = true;
+      if (needCanonical)
+      {
+        notQueriedTDBYet = false;
+      }
+    }
+  };
+
+  /**
    * Initializes parameters used by the Structure Chooser Panel
    */
   protected void init()
@@ -127,115 +182,146 @@ public class StructureChooser extends GStructureChooser
 
     chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
 
-    final Runnable discoverPDBStructures = new Runnable()
+    Executors.defaultThreadFactory().newThread(new Runnable()
     {
-      @Override
       public void run()
       {
-        // check which FTS engine to use
-        data = StructureChooserQuerySource
-                .getQuerySourceFor(selectedSequences);
+        populateSeqsWithoutSourceDBRef();
+        initialStructureDiscovery();
+      }
+
+    }).start();
 
-        // ensure a filter option is in force for search
-        populateFilterComboBox(true, cachedPDBExists);
+  }
 
-        // looks for any existing structures already loaded
-        // for the sequences (the cached ones)
-        // then queries the StructureChooserQuerySource to
-        // discover more structures.
-        //
-        // Possible optimisation is to only begin querying
-        // the structure chooser if there are no cached structures.
+  // called by init
+  private void initialStructureDiscovery()
+  {
+    // check which FTS engine to use
+    data = StructureChooserQuerySource.getQuerySourceFor(selectedSequences);
 
-        long startTime = System.currentTimeMillis();
-        updateProgressIndicator(MessageManager
-                .getString("status.loading_cached_pdb_entries"), startTime);
-        loadLocalCachedPDBEntries();
-        updateProgressIndicator(null, startTime);
-        updateProgressIndicator(MessageManager.getString(
-                "status.searching_for_pdb_structures"), startTime);
-        fetchStructuresMetaData();
-        // revise filter options if no results were found
-        populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
-        discoverStructureViews();
-        updateProgressIndicator(null, startTime);
-        mainFrame.setVisible(true);
-        updateCurrentView();
-      }
-    };
-    final List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
+    // ensure a filter option is in force for search
+    populateFilterComboBox(true, cachedPDBExists);
 
-    final Runnable discoverCanonicalDBrefs = new Runnable()
+    // looks for any existing structures already loaded
+    // for the sequences (the cached ones)
+    // then queries the StructureChooserQuerySource to
+    // discover more structures.
+    //
+    // Possible optimisation is to only begin querying
+    // the structure chooser if there are no cached structures.
+
+    long startTime = System.currentTimeMillis();
+    updateProgressIndicator(
+            MessageManager.getString("status.loading_cached_pdb_entries"),
+            startTime);
+    loadLocalCachedPDBEntries();
+    updateProgressIndicator(null, startTime);
+    updateProgressIndicator(
+            MessageManager.getString("status.searching_for_pdb_structures"),
+            startTime);
+    fetchStructuresMetaData();
+    // revise filter options if no results were found
+    populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
+    discoverStructureViews();
+    updateProgressIndicator(null, startTime);
+    mainFrame.setVisible(true);
+    updateCurrentView();
+  }
+
+  private void promptForTDBFetch()
+  {
+    final long progressId = System.currentTimeMillis();
+
+    // final action after prompting and discovering db refs
+    final Runnable strucDiscovery = new Runnable()
     {
       @Override
       public void run()
       {
-        long progressId = System.currentTimeMillis();
-
-        int y = seqsWithoutSourceDBRef.size();
-        
-        setProgressBar(MessageManager.formatMessage(
-                "status.fetching_dbrefs_for_sequences_without_valid_refs",
-                y), progressId);
-        SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
-                .toArray(new SequenceI[y]);
+        // TODO: warn if no accessions discovered
+        populateSeqsWithoutSourceDBRef();
+        // redo initial discovery - this time with 3d beacons
+        // Executors.
+        previousWantedFields=null;
         
-        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
-                progressBar, new DbSourceProxy[]
-                { new jalview.ws.dbsources.Uniprot() },
-                null, false);
-        
-        dbRefFetcher.fetchDBRefs(true);
-
-        setProgressBar("Fetch complete.", progressId); // todo i18n
-
-        Executors.defaultThreadFactory().newThread(discoverPDBStructures).start();
+        initialStructureDiscovery();
       }
     };
 
-    Executors.defaultThreadFactory().newThread(new Runnable()
+    // fetch db refs if OK pressed
+    final Runnable discoverCanonicalDBrefs = new Runnable()
     {
+      @Override
       public void run()
       {
+        populateSeqsWithoutSourceDBRef();
 
-        for (SequenceI seq : selectedSequences)
+        final int y = seqsWithoutSourceDBRef.size();
+        if (y > 0)
         {
-          if (seq.isProtein())
-          {
-            int dbRef = ThreeDBStructureChooserQuerySource
-                    .checkUniprotRefs(seq.getDBRefs());
-            if (dbRef < 0)
+          SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run()
             {
-              if (seq.getAllPDBEntries()==null && seq.getAllPDBEntries().size()==0)
-              {
-                seqsWithoutSourceDBRef.add(seq);
-              }
+              updateProgressIndicator(MessageManager.formatMessage(
+                      "status.fetching_dbrefs_for_sequences_without_valid_refs",
+                      y), progressId);
+              
             }
-          }
+          });
+          
+          final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                  .toArray(new SequenceI[y]);
+          DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
+                  progressBar, new DbSourceProxy[]
+                  { new jalview.ws.dbsources.Uniprot() }, null, false);
+
+          // ideally this would also gracefully run with callbacks
+          dbRefFetcher.fetchDBRefs(true);
         }
-        // retrieve database refs for protein sequences
-        if (!seqsWithoutSourceDBRef.isEmpty())
-        {
-          // need cancel and no to result in the discoverPDB action - mocked is 'cancel'
-          JvOptionPane.newOptionDialog(Desktop.getDesktop())
-                  .setResponseHandler(JvOptionPane.OK_OPTION, discoverCanonicalDBrefs)
-                  .setResponseHandler(JvOptionPane.CANCEL_OPTION, discoverPDBStructures)
-                  .setResponseHandler(JvOptionPane.NO_OPTION, discoverPDBStructures)
-                  .showDialog(MessageManager.formatMessage("label.fetch_references_for",
-                          seqsWithoutSourceDBRef.size()), MessageManager.getString(
-                          "label.fetch_uniprot_references"),
-                          JvOptionPane.YES_NO_OPTION,
-                          JvOptionPane.PLAIN_MESSAGE, null, new Object[]
-                          { MessageManager.getString("action.ok"),
-                              MessageManager.getString("action.cancel") },
-                          MessageManager.getString("action.ok"));
-        } else {
-          // get structures directly
-          Executors.defaultThreadFactory().newThread(discoverPDBStructures).start();
+        SwingUtilities.invokeLater(new Runnable() {
+          @Override
+          public void run()
+          {
+            if (y>0) {
+              updateProgressIndicator("Fetch complete.", progressId); // todo i18n
+            }
+            // filter has been selected, so we set flag to remove ourselves
+            notQueriedTDBYet = false;
+
+            Executors.defaultThreadFactory().newThread(strucDiscovery).start();
+
+          }
+        });
+        
+      }
+    };
+    final Runnable revertview = new Runnable() {
+      public void run() {
+        if (lastSelected!=null) {
+          cmb_filterOption.setSelectedItem(lastSelected);
         }
       };
-    }).start();;
-
+    };
+    // need cancel and no to result in the discoverPDB action - mocked is
+    // 'cancel'
+    JvOptionPane.newOptionDialog(this)
+            .setResponseHandler(JvOptionPane.OK_OPTION,
+                    discoverCanonicalDBrefs)
+            .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
+            .setResponseHandler(JvOptionPane.NO_OPTION, revertview)
+            .showDialog(
+                    MessageManager.formatMessage(
+                            "label.fetch_references_for_3dbeacons",
+                            seqsWithoutSourceDBRef.size()),
+                    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"));
   }
 
   /**
@@ -331,8 +417,9 @@ public class StructureChooser extends GStructureChooser
         resultList = data.fetchStructuresMetaData(seq, wantedFields,
                 selectedFilterOpt, !chk_invertFilter.isSelected());
         // null response means the FTSengine didn't yield a query for this
-        // consider designing a special exception if we really wanted to be OOCrazy
-        if (resultList==null)
+        // consider designing a special exception if we really wanted to be
+        // OOCrazy
+        if (resultList == null)
         {
           continue;
         }
@@ -357,9 +444,9 @@ public class StructureChooser extends GStructureChooser
     {
       getResultTable()
               .setModel(data.getTableModel(discoveredStructuresSet));
-      
+
       noOfStructuresFound = discoveredStructuresSet.size();
-      lastDiscoveredStructuresSet=discoveredStructuresSet;
+      lastDiscoveredStructuresSet = discoveredStructuresSet;
       mainFrame.setTitle(MessageManager.formatMessage(
               "label.structure_chooser_no_of_structures",
               noOfStructuresFound, totalTime));
@@ -432,8 +519,9 @@ public class StructureChooser extends GStructureChooser
           FTSRestResponse resultList;
           try
           {
-            resultList = data.selectFirstRankedQuery(seq, discoveredStructuresSet,wantedFields,
-                    fieldToFilterBy, !chk_invertFilter.isSelected());
+            resultList = data.selectFirstRankedQuery(seq,
+                    discoveredStructuresSet, wantedFields, fieldToFilterBy,
+                    !chk_invertFilter.isSelected());
 
           } catch (Exception e)
           {
@@ -458,7 +546,7 @@ public class StructureChooser extends GStructureChooser
           reorderedStructuresSet.addAll(discoveredStructuresSet);
           getResultTable()
                   .setModel(data.getTableModel(reorderedStructuresSet));
-          
+
           FTSRestResponse.configureTableColumn(getResultTable(),
                   wantedFields, tempUserPrefs);
           getResultTable().getColumn("Ref Sequence").setPreferredWidth(120);
@@ -532,6 +620,7 @@ public class StructureChooser extends GStructureChooser
   {
     populateFilterComboBox(haveData, cachedPDBExist, null);
   }
+
   /**
    * Populates the filter combo-box options dynamically depending on discovered
    * structures
@@ -539,45 +628,57 @@ public class StructureChooser extends GStructureChooser
   protected void populateFilterComboBox(boolean haveData,
           boolean cachedPDBExist, FilterOption lastSel)
   {
-    
+
     /*
      * temporarily suspend the change listener behaviour
      */
     cmb_filterOption.removeItemListener(this);
-    int selSet=-1;
+    int selSet = -1;
     cmb_filterOption.removeAllItems();
     if (haveData)
     {
-      List<FilterOption> filters = data.getAvailableFilterOptions(VIEWS_FILTER);
-      data.updateAvailableFilterOptions(VIEWS_FILTER, filters, lastDiscoveredStructuresSet);
-      int p=0;
-      for (FilterOption filter:filters)
+      List<FilterOption> filters = data
+              .getAvailableFilterOptions(VIEWS_FILTER);
+      data.updateAvailableFilterOptions(VIEWS_FILTER, filters,
+              lastDiscoveredStructuresSet);
+      int p = 0;
+      for (FilterOption filter : filters)
       {
-        if (lastSel!=null && filter.equals(lastSel)) {
-          selSet=p;
+        if (lastSel != null && filter.equals(lastSel))
+        {
+          selSet = p;
         }
         p++;
         cmb_filterOption.addItem(filter);
       }
     }
+
     cmb_filterOption.addItem(
             new FilterOption(MessageManager.getString("label.enter_pdb_id"),
-                    "-", VIEWS_ENTER_ID, false,null));
+                    "-", VIEWS_ENTER_ID, false, null));
     cmb_filterOption.addItem(
             new FilterOption(MessageManager.getString("label.from_file"),
-                    "-", VIEWS_FROM_FILE, false,null));
+                    "-", VIEWS_FROM_FILE, false, null));
+    if (canQueryTDB && notQueriedTDBYet)
+    {
+      FilterOption queryTDBOption = new FilterOption(
+              MessageManager.getString("label.search_3dbeacons"), "-",
+              VIEWS_QUERYING_TDB, false, null);
+      cmb_filterOption.addItem(queryTDBOption);
+    }
 
     if (cachedPDBExist)
     {
       FilterOption cachedOption = new FilterOption(
               MessageManager.getString("label.cached_structures"), "-",
-              VIEWS_LOCAL_PDB, false,null);
+              VIEWS_LOCAL_PDB, false, null);
       cmb_filterOption.addItem(cachedOption);
-      if (selSet==-1) {
+      if (selSet == -1)
+      {
         cmb_filterOption.setSelectedItem(cachedOption);
       }
     }
-    if (selSet>-1)
+    if (selSet > -1)
     {
       cmb_filterOption.setSelectedIndex(selSet);
     }
@@ -591,24 +692,45 @@ public class StructureChooser extends GStructureChooser
   {
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
             .getSelectedItem());
+    
+    // first check if we need to rebuild dialog
+    if (selectedFilterOpt.getView() == VIEWS_QUERYING_TDB)
+    {
+      promptForTDBFetch();
+      return;
+    }
+    if (lastSelected == selectedFilterOpt)
+    {
+      // don't need to do anything, probably
+      return;
+    }
+    // otherwise, record selection
+    // and update the layout and dialog accordingly
+    lastSelected = selectedFilterOpt;
+
     layout_switchableViews.show(pnl_switchableViews,
             selectedFilterOpt.getView());
     String filterTitle = mainFrame.getTitle();
     mainFrame.setTitle(frameTitle);
     chk_invertFilter.setVisible(false);
+    
     if (selectedFilterOpt.getView() == VIEWS_FILTER)
     {
       mainFrame.setTitle(filterTitle);
       // TDB Query has no invert as yet
-      chk_invertFilter.setVisible(selectedFilterOpt.getQuerySource() instanceof PDBStructureChooserQuerySource);
-      
-      if (data!=selectedFilterOpt.getQuerySource() || data.needsRefetch(selectedFilterOpt)) 
+      chk_invertFilter.setVisible(selectedFilterOpt
+              .getQuerySource() instanceof PDBStructureChooserQuerySource);
+
+      if (data != selectedFilterOpt.getQuerySource()
+              || data.needsRefetch(selectedFilterOpt))
       {
         data = selectedFilterOpt.getQuerySource();
         // rebuild the views completely, since prefs will also change
         tabRefresh();
         return;
-      } else {
+      }
+      else
+      {
         filterResultSet(selectedFilterOpt.getValue());
       }
     }
@@ -747,7 +869,7 @@ public class StructureChooser extends GStructureChooser
   {
     validateSelections();
   }
-
+  private FilterOption lastSelected=null;
   /**
    * Handles the state change event for the 'filter' combo-box and 'invert'
    * check-box
@@ -855,7 +977,8 @@ public class StructureChooser extends GStructureChooser
           int[] selectedRows = restable.getSelectedRows();
           PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
           List<SequenceI> selectedSeqsToView = new ArrayList<>();
-          pdbEntriesToView = data.collectSelectedRows(restable,selectedRows,selectedSeqsToView);
+          pdbEntriesToView = data.collectSelectedRows(restable,
+                  selectedRows, selectedSeqsToView);
 
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
@@ -874,8 +997,9 @@ public class StructureChooser extends GStructureChooser
           List<SequenceI> selectedSeqsToView = new ArrayList<>();
           for (int row : selectedRows)
           {
-            PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb.getModel()).getPDBEntryAt(row).getPdbEntry();
-            
+            PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb
+                    .getModel()).getPDBEntryAt(row).getPdbEntry();
+
             pdbEntriesToView[count++] = pdbEntry;
             SequenceI selectedSeq = (SequenceI) tbl_local_pdb
                     .getValueAt(row, refSeqColIndex);
@@ -1176,7 +1300,7 @@ public class StructureChooser extends GStructureChooser
         public void run()
         {
           fetchStructuresMetaData();
-          //populateFilterComboBox(true, cachedPDBExists);
+          // populateFilterComboBox(true, cachedPDBExists);
 
           filterResultSet(
                   ((FilterOption) cmb_filterOption.getSelectedItem())
@@ -1279,8 +1403,8 @@ public class StructureChooser extends GStructureChooser
     {
       if (pdbEntry.hasProvider())
       {
-        return pdbEntry.getProvider()+":"+pdbEntry.getId();
-      } 
+        return pdbEntry.getProvider() + ":" + pdbEntry.getId();
+      }
       return pdbEntry.toString();
     }
 
@@ -1325,12 +1449,13 @@ public class StructureChooser extends GStructureChooser
   protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
   {
     data.setDocFieldPrefs(newPrefs);
-    
+
   }
 
   /**
    * 
-   * @return true when all initialisation threads have finished and dialog is visible
+   * @return true when all initialisation threads have finished and dialog is
+   *         visible
    */
   public boolean isDialogVisible()
   {
index 45c58cf..a4ae2c8 100644 (file)
@@ -42,7 +42,7 @@ public abstract class StructureChooserQuerySource
 
   public static StructureChooserQuerySource getPDBfts()
   {
-          return new PDBStructureChooserQuerySource();
+    return new PDBStructureChooserQuerySource();
   }
 
   public static StructureChooserQuerySource getTDBfts()
@@ -65,7 +65,6 @@ public abstract class StructureChooserQuerySource
     return docFieldPrefs;
   }
 
-
   /**
    * Builds a query string for a given sequences using its DBRef entries
    * 
@@ -75,7 +74,6 @@ public abstract class StructureChooserQuerySource
    */
 
   public abstract String buildQuery(SequenceI seq);
-  
 
   /**
    * Remove the following special characters from input string +, -, &, !, (, ),
@@ -167,7 +165,8 @@ public abstract class StructureChooserQuerySource
    * 
    * @param seq
    *          - seq to generate a query for
-   * @param discoveredStructuresSet - existing set of entries - allows client side selection
+   * @param discoveredStructuresSet
+   *          - existing set of entries - allows client side selection
    * @param wantedFields
    *          - fields to retrieve
    * @param selectedFilterOpt
@@ -178,7 +177,8 @@ public abstract class StructureChooserQuerySource
    * @throws Exception
    */
   public abstract FTSRestResponse selectFirstRankedQuery(SequenceI seq,
-          Collection<FTSData> discoveredStructuresSet, Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
+          Collection<FTSData> discoveredStructuresSet,
+          Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
           boolean b) throws Exception;
 
   /**
@@ -200,15 +200,17 @@ public abstract class StructureChooserQuerySource
 
   /**
    * @param VIEWS_FILTER
-   *          - a String key that can be used by the caller to tag the returned filter
-   *          options to distinguish them in a collection
+   *          - a String key that can be used by the caller to tag the returned
+   *          filter options to distinguish them in a collection
    * @return list of FilterOption - convention is that the last one in the list
    *         will be constructed with 'addSeparator==true'
    */
-  public abstract List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER);
+  public abstract List<FilterOption> getAvailableFilterOptions(
+          String VIEWS_FILTER);
 
   /**
    * construct a structure chooser query source for the given set of sequences
+   * 
    * @param selectedSeqs
    * @return PDBe or 3DB query source
    */
@@ -216,34 +218,39 @@ public abstract class StructureChooserQuerySource
           SequenceI[] selectedSeqs)
   {
     ThreeDBStructureChooserQuerySource tdbSource = new ThreeDBStructureChooserQuerySource();
-    boolean hasUniprot=false,hasCanonical=false;
-    boolean hasNA=false,hasProtein=false;
-    int protWithoutUni=0;
+    boolean hasUniprot = false, hasCanonical = false;
+    boolean hasNA = false, hasProtein = false;
+    int protWithoutUni = 0;
+    int protWithoutCanon = 0;
     for (SequenceI seq : selectedSeqs)
     {
       hasNA |= !seq.isProtein();
       hasProtein |= seq.isProtein();
       if (seq.isProtein())
       {
-        int refsAvailable = ThreeDBStructureChooserQuerySource.checkUniprotRefs(seq.getDBRefs());
+        int refsAvailable = ThreeDBStructureChooserQuerySource
+                .checkUniprotRefs(seq.getDBRefs());
         if (refsAvailable > -2)
         {
           if (refsAvailable > -1)
+          {
             hasCanonical = true;
+          } else {
+            protWithoutCanon++;
+          }
+          hasUniprot = true;
+        } else {
+          protWithoutUni++;
+          
         }
-        hasUniprot = true;
-      }
-      else
-      {
-        protWithoutUni++;
       }
     }
     //
     // logic: all canonicals - no fetchdb
-    // some uniprot no canonicals: prompt do fetchDb for remaining
-    // no uniprot but protein: offer 3d-beacons search
+    // some uniprot no canonicals: defer to PDB, user can optionally fetch
     //
-    if (hasProtein && hasCanonical && !hasNA)
+    if (hasProtein && hasCanonical && !hasNA && protWithoutCanon == 0 && protWithoutUni == 0)
+
     {
       return tdbSource;
     }
@@ -251,17 +258,17 @@ public abstract class StructureChooserQuerySource
   }
 
   /**
-   * some filter options may mean the original query needs to be executed again. 
+   * some filter options may mean the original query needs to be executed again.
+   * 
    * @param selectedFilterOpt
    * @return true if the fetchStructuresMetadata method needs to be called again
    */
   public abstract boolean needsRefetch(FilterOption selectedFilterOpt);
 
-  
-  
   public void updateAvailableFilterOptions(String VIEWS_FILTER,
           List<FilterOption> xtantOptions, Collection<FTSData> lastFTSData)
   {
     // TODO Auto-generated method stub
-    
-  }}
\ No newline at end of file
+
+  }
+}
\ No newline at end of file