Clover icon

Coverage Report

  1. Project Clover database Fri Nov 15 2024 13:56:46 GMT
  2. Package jalview.gui

File StructureChooser.java

 

Coverage histogram

../../img/srcFileCovDistChart4.png
48% of files have more coverage

Code metrics

226
621
73
3
1,914
1,481
223
0.36
8.51
24.33
3.05

Classes

Class Line # Actions
StructureChooser 98 588 203
0.3855005838.6%
StructureChooser.PDBEntryTableModel 1627 26 15
0.2105263221.1%
StructureChooser.CachedPDB 1703 7 5
0.00%
 

Contributing tests

This file is covered by 36 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21   
22    package jalview.gui;
23   
24    import java.awt.event.ActionEvent;
25    import java.awt.event.ActionListener;
26    import java.awt.event.ItemEvent;
27    import java.io.File;
28    import java.util.ArrayList;
29    import java.util.Collection;
30    import java.util.HashSet;
31    import java.util.LinkedHashMap;
32    import java.util.LinkedHashSet;
33    import java.util.List;
34    import java.util.Locale;
35    import java.util.Map;
36    import java.util.concurrent.Executors;
37   
38    import javax.swing.JCheckBox;
39    import javax.swing.JComboBox;
40    import javax.swing.JLabel;
41    import javax.swing.JMenuItem;
42    import javax.swing.JPopupMenu;
43    import javax.swing.JProgressBar;
44    import javax.swing.JTable;
45    import javax.swing.SwingUtilities;
46    import javax.swing.table.AbstractTableModel;
47   
48    import com.stevesoft.pat.Regex;
49   
50    import jalview.analysis.AlignmentUtils;
51    import jalview.api.AlignmentViewPanel;
52    import jalview.api.structures.JalviewStructureDisplayI;
53    import jalview.bin.Cache;
54    import jalview.bin.Console;
55    import jalview.bin.Jalview;
56    import jalview.datamodel.AlignmentAnnotation;
57    import jalview.datamodel.AlignmentI;
58    import jalview.datamodel.PDBEntry;
59    import jalview.datamodel.SequenceGroup;
60    import jalview.datamodel.SequenceI;
61    import jalview.ext.jmol.JmolParser;
62    import jalview.fts.api.FTSData;
63    import jalview.fts.api.FTSDataColumnI;
64    import jalview.fts.api.FTSRestClientI;
65    import jalview.fts.core.FTSDataColumnPreferences;
66    import jalview.fts.core.FTSRestRequest;
67    import jalview.fts.core.FTSRestResponse;
68    import jalview.fts.service.pdb.PDBFTSRestClient;
69    import jalview.fts.service.threedbeacons.TDB_FTSData;
70    import jalview.gui.StructureViewer.ViewerType;
71    import jalview.gui.structurechooser.PDBStructureChooserQuerySource;
72    import jalview.gui.structurechooser.StructureChooserQuerySource;
73    import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource;
74    import jalview.io.DataSourceType;
75    import jalview.io.JalviewFileChooser;
76    import jalview.io.JalviewFileView;
77    import jalview.jbgui.FilterOption;
78    import jalview.jbgui.GStructureChooser;
79    import jalview.structure.StructureImportSettings.TFType;
80    import jalview.structure.StructureMapping;
81    import jalview.structure.StructureSelectionManager;
82    import jalview.util.MessageManager;
83    import jalview.util.Platform;
84    import jalview.util.StringUtils;
85    import jalview.ws.DBRefFetcher;
86    import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
87    import jalview.ws.datamodel.alphafold.PAEContactMatrix;
88    import jalview.ws.seqfetcher.DbSourceProxy;
89    import jalview.ws.sifts.SiftsSettings;
90   
91    /**
92    * Provides the behaviors for the Structure chooser Panel
93    *
94    * @author tcnofoegbu
95    *
96    */
97    @SuppressWarnings("serial")
 
98    public class StructureChooser extends GStructureChooser
99    implements IProgressIndicator
100    {
101    private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
102   
103    /**
104    * warn user if need to fetch more than this many uniprot records at once
105    */
106    private static final int THRESHOLD_WARN_UNIPROT_FETCH_NEEDED = 20;
107   
108    private SequenceI selectedSequence;
109   
110    private SequenceI[] selectedSequences;
111   
112    private IProgressIndicator progressIndicator;
113   
114    private Collection<FTSData> discoveredStructuresSet;
115   
116    private StructureChooserQuerySource data;
117   
 
118  55 toggle @Override
119    protected FTSDataColumnPreferences getFTSDocFieldPrefs()
120    {
121  55 return data.getDocFieldPrefs();
122    }
123   
124    private String selectedPdbFileName;
125   
126    private String localPdbPaeMatrixFileName;
127   
128    private boolean isValidPBDEntry;
129   
130    private boolean cachedPDBExists;
131   
132    private Collection<FTSData> lastDiscoveredStructuresSet;
133   
134    private boolean canQueryTDB = false;
135   
136    private boolean notQueriedTDBYet = true;
137   
138    List<SequenceI> seqsWithoutSourceDBRef = null;
139   
140    private boolean showChooserGUI = true;
141   
142    /**
143    * when true, queries to external services are supressed (no SIFTs, no PDBe,
144    * no 3D-Beacons, etc)
145    */
146    private boolean dontQueryServices = false;
147   
148    private static StructureViewer lastTargetedView = null;
149   
 
150  3 toggle public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
151    AlignmentPanel ap)
152    {
153  3 this(selectedSeqs, selectedSeq, ap, true);
154    }
155   
 
156  3 toggle public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
157    AlignmentPanel ap, boolean showGUI)
158    {
159  3 this(selectedSeqs, selectedSeq, ap, showGUI, false);
160    }
161   
 
162  55 toggle public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
163    AlignmentPanel ap, boolean showGUI, boolean dontQueryServices)
164    {
165   
166    // which FTS engine to use
167  55 data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
168  55 initDialog();
169   
170  55 this.ap = ap;
171  55 this.selectedSequence = selectedSeq;
172  55 this.selectedSequences = selectedSeqs;
173  55 this.progressIndicator = (ap == null) ? null : ap.alignFrame;
174  55 this.showChooserGUI = showGUI;
175  55 this.dontQueryServices = dontQueryServices;
176  55 init();
177   
178    }
179   
180    /**
181    * sets canQueryTDB if protein sequences without a canonical uniprot ref or at
182    * least one structure are discovered.
183    */
 
184  3 toggle private void populateSeqsWithoutSourceDBRef()
185    {
186  3 seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
187  3 boolean needCanonical = false;
188  3 for (SequenceI seq : selectedSequences)
189    {
190  3 if (seq.isProtein())
191    {
192  3 int dbRef = ThreeDBStructureChooserQuerySource
193    .checkUniprotRefs(seq.getDBRefs());
194  3 if (dbRef < 0)
195    {
196  2 if (dbRef == -1)
197    {
198    // need to retrieve canonicals
199  1 needCanonical = true;
200  1 seqsWithoutSourceDBRef.add(seq);
201    }
202    else
203    {
204    // could be a sequence with pdb ref
205  1 if (seq.getAllPDBEntries() == null
206    || seq.getAllPDBEntries().size() == 0)
207    {
208  0 seqsWithoutSourceDBRef.add(seq);
209    }
210    }
211    }
212    }
213    }
214    // retrieve database refs for protein sequences
215  3 if (!seqsWithoutSourceDBRef.isEmpty())
216    {
217  1 canQueryTDB = true;
218  1 if (needCanonical)
219    {
220    // triggers display of the 'Query TDB' button
221  1 notQueriedTDBYet = true;
222    }
223    }
224    };
225   
226    /**
227    * Initializes parameters used by the Structure Chooser Panel
228    */
 
229  55 toggle protected void init()
230    {
231  55 if (!Jalview.isHeadlessMode())
232    {
233  36 progressBar = new ProgressBar(this.statusPanel, this.statusBar);
234    }
235   
236  55 chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
237  55 btn_queryTDB.addActionListener(new ActionListener()
238    {
239   
 
240  0 toggle @Override
241    public void actionPerformed(ActionEvent e)
242    {
243  0 promptForTDBFetch(false);
244    }
245    });
246   
247  55 if (!dontQueryServices)
248    {
249  3 Executors.defaultThreadFactory().newThread(new Runnable()
250    {
 
251  3 toggle @Override
252    public void run()
253    {
254  3 populateSeqsWithoutSourceDBRef();
255  3 initialStructureDiscovery();
256    }
257   
258    }).start();
259    }
260    else
261    {
262  52 Console.debug(
263    "Structure chooser not querying services to discover metadata.");
264    }
265    }
266   
267    // called by init
 
268  3 toggle private void initialStructureDiscovery()
269    {
270    // check which FTS engine to use
271  3 data = StructureChooserQuerySource.getQuerySourceFor(selectedSequences);
272   
273    // ensure a filter option is in force for search
274  3 populateFilterComboBox(true, cachedPDBExists);
275   
276    // looks for any existing structures already loaded
277    // for the sequences (the cached ones)
278    // then queries the StructureChooserQuerySource to
279    // discover more structures.
280    //
281    // Possible optimisation is to only begin querying
282    // the structure chooser if there are no cached structures.
283   
284  3 long startTime = System.currentTimeMillis();
285  3 updateProgressIndicator(
286    MessageManager.getString("status.loading_cached_pdb_entries"),
287    startTime);
288  3 loadLocalCachedPDBEntries();
289  3 updateProgressIndicator(null, startTime);
290  3 updateProgressIndicator(
291    MessageManager.getString("status.searching_for_pdb_structures"),
292    startTime);
293  3 fetchStructuresMetaData();
294    // revise filter options if no results were found
295  3 populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
296  3 discoverStructureViews();
297  2 updateProgressIndicator(null, startTime);
298  2 mainFrame.setVisible(showChooserGUI);
299  2 updateCurrentView();
300    }
301   
302    /**
303    * raises dialog for Uniprot fetch followed by 3D beacons search
304    *
305    * @param ignoreGui
306    * - when true, don't ask, just fetch
307    */
 
308  0 toggle public void promptForTDBFetch(boolean ignoreGui)
309    {
310  0 final long progressId = System.currentTimeMillis();
311   
312    // final action after prompting and discovering db refs
313  0 final Runnable strucDiscovery = new Runnable()
314    {
 
315  0 toggle @Override
316    public void run()
317    {
318  0 mainFrame.setEnabled(false);
319  0 cmb_filterOption.setEnabled(false);
320  0 progressBar.setProgressBar(
321    MessageManager.getString("status.searching_3d_beacons"),
322    progressId);
323  0 btn_queryTDB.setEnabled(false);
324    // TODO: warn if no accessions discovered
325  0 populateSeqsWithoutSourceDBRef();
326    // redo initial discovery - this time with 3d beacons
327    // Executors.
328  0 previousWantedFields = null;
329  0 lastSelected = (FilterOption) cmb_filterOption.getSelectedItem();
330  0 cmb_filterOption.setSelectedItem(null);
331  0 cachedPDBExists = false; // reset to initial
332  0 initialStructureDiscovery();
333  0 if (!isStructuresDiscovered())
334    {
335  0 progressBar.setProgressBar(MessageManager.getString(
336    "status.no_structures_discovered_from_3d_beacons"),
337    progressId);
338  0 btn_queryTDB.setToolTipText(MessageManager.getString(
339    "status.no_structures_discovered_from_3d_beacons"));
340  0 btn_queryTDB.setEnabled(false);
341  0 pnl_queryTDB.setVisible(false);
342    }
343    else
344    {
345  0 cmb_filterOption.setSelectedIndex(0); // select 'best'
346  0 btn_queryTDB.setVisible(false);
347  0 pnl_queryTDB.setVisible(false);
348  0 progressBar.setProgressBar(null, progressId);
349    }
350  0 mainFrame.setEnabled(true);
351  0 cmb_filterOption.setEnabled(true);
352    }
353    };
354   
355  0 final FetchFinishedListenerI afterDbRefFetch = new FetchFinishedListenerI()
356    {
357   
 
358  0 toggle @Override
359    public void finished()
360    {
361    // filter has been selected, so we set flag to remove ourselves
362  0 notQueriedTDBYet = false;
363    // new thread to discover structures - via 3d beacons
364  0 Executors.defaultThreadFactory().newThread(strucDiscovery).start();
365   
366    }
367    };
368   
369    // fetch db refs if OK pressed
370  0 final Runnable discoverCanonicalDBrefs = () -> {
371  0 btn_queryTDB.setEnabled(false);
372  0 populateSeqsWithoutSourceDBRef();
373   
374  0 final int y = seqsWithoutSourceDBRef.size();
375  0 if (y > 0)
376    {
377  0 final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
378    .toArray(new SequenceI[y]);
379  0 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
380    progressBar, new DbSourceProxy[]
381    { new jalview.ws.dbsources.Uniprot() }, null, false);
382  0 dbRefFetcher.addListener(afterDbRefFetch);
383    // ideally this would also gracefully run with callbacks
384   
385  0 dbRefFetcher.fetchDBRefs(true);
386    }
387    else
388    {
389    // call finished action directly
390  0 afterDbRefFetch.finished();
391    }
392    };
393  0 final Runnable revertview = () -> {
394  0 if (lastSelected != null)
395    {
396  0 cmb_filterOption.setSelectedItem(lastSelected);
397    }
398    };
399  0 int threshold = Cache.getDefault("UNIPROT_AUTOFETCH_THRESHOLD",
400    THRESHOLD_WARN_UNIPROT_FETCH_NEEDED);
401  0 Console.debug("Using Uniprot fetch threshold of " + threshold);
402  0 if (ignoreGui || seqsWithoutSourceDBRef.size() < threshold)
403    {
404  0 Executors.newSingleThreadExecutor().submit(discoverCanonicalDBrefs);
405  0 return;
406    }
407    // need cancel and no to result in the discoverPDB action - mocked is
408    // 'cancel' TODO: mock should be OK
409   
410  0 StructureChooser thisSC = this;
411  0 JvOptionPane.newOptionDialog(thisSC.getFrame())
412    .setResponseHandler(JvOptionPane.OK_OPTION,
413    discoverCanonicalDBrefs)
414    .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
415    .setResponseHandler(JvOptionPane.NO_OPTION, revertview)
416    .showDialog(
417    MessageManager.formatMessage(
418    "label.fetch_references_for_3dbeacons",
419    seqsWithoutSourceDBRef.size()),
420    MessageManager.getString("label.3dbeacons"),
421    JvOptionPane.YES_NO_OPTION, JvOptionPane.PLAIN_MESSAGE,
422    null, new Object[]
423    { MessageManager.getString("action.ok"),
424    MessageManager.getString("action.cancel") },
425    MessageManager.getString("action.ok"), false);
426    }
427   
428    /**
429    * Builds a drop-down choice list of existing structure viewers to which new
430    * structures may be added. If this list is empty then it, and the 'Add'
431    * button, are hidden.
432    */
 
433  3 toggle private void discoverStructureViews()
434    {
435  3 if (Desktop.instance != null)
436    {
437  3 targetView.removeAllItems();
438  3 if (lastTargetedView != null && !lastTargetedView.isVisible())
439    {
440  0 lastTargetedView = null;
441    }
442  3 int linkedViewsAt = 0;
443  3 for (StructureViewerBase view : Desktop.instance
444    .getStructureViewers(null, null))
445    {
446  1 StructureViewer viewHandler = (lastTargetedView != null
447    && lastTargetedView.sview == view) ? lastTargetedView
448    : StructureViewer.reconfigure(view);
449   
450  0 if (view.isLinkedWith(ap))
451    {
452  0 targetView.insertItemAt(viewHandler, linkedViewsAt++);
453    }
454    else
455    {
456  0 targetView.addItem(viewHandler);
457    }
458    }
459   
460    /*
461    * show option to Add to viewer if at least 1 viewer found
462    */
463  2 targetView.setVisible(false);
464  2 if (targetView.getItemCount() > 0)
465    {
466  0 targetView.setVisible(true);
467  0 if (lastTargetedView != null)
468    {
469  0 targetView.setSelectedItem(lastTargetedView);
470    }
471    else
472    {
473  0 targetView.setSelectedIndex(0);
474    }
475    }
476  2 btn_add.setVisible(targetView.isVisible());
477    }
478    }
479   
480    /**
481    * Updates the progress indicator with the specified message
482    *
483    * @param message
484    * displayed message for the operation
485    * @param id
486    * unique handle for this indicator
487    */
 
488  11 toggle protected void updateProgressIndicator(String message, long id)
489    {
490  11 if (progressIndicator != null)
491    {
492  0 progressIndicator.setProgressBar(message, id);
493    }
494    }
495   
496    /**
497    * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
498    * selection group
499    */
 
500  4 toggle void fetchStructuresMetaData()
501    {
502  4 long startTime = System.currentTimeMillis();
503  4 Collection<FTSDataColumnI> wantedFields = data.getDocFieldPrefs()
504    .getStructureSummaryFields();
505   
506  4 discoveredStructuresSet = new LinkedHashSet<>();
507  4 HashSet<String> errors = new HashSet<>();
508   
509  4 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
510    .getSelectedItem());
511   
512  4 for (SequenceI seq : selectedSequences)
513    {
514   
515  4 FTSRestResponse resultList;
516  4 try
517    {
518  4 resultList = data.fetchStructuresMetaData(seq, wantedFields,
519    selectedFilterOpt, !chk_invertFilter.isSelected());
520    // null response means the FTSengine didn't yield a query for this
521    // consider designing a special exception if we really wanted to be
522    // OOCrazy
523  2 if (resultList == null)
524    {
525  0 continue;
526    }
527    } catch (Exception e)
528    {
529  2 Console.printStackTrace(e);
530  2 errors.add(e.getMessage());
531  2 continue;
532    }
533  2 if (resultList.getSearchSummary() != null
534    && !resultList.getSearchSummary().isEmpty())
535    {
536  2 discoveredStructuresSet.addAll(resultList.getSearchSummary());
537    }
538    }
539   
540  4 int noOfStructuresFound = 0;
541  4 String totalTime = (System.currentTimeMillis() - startTime)
542    + " milli secs";
543  4 if (discoveredStructuresSet != null
544    && !discoveredStructuresSet.isEmpty())
545    {
546  2 getResultTable()
547    .setModel(data.getTableModel(discoveredStructuresSet));
548   
549  2 noOfStructuresFound = discoveredStructuresSet.size();
550  2 lastDiscoveredStructuresSet = discoveredStructuresSet;
551  2 mainFrame.setTitle(MessageManager.formatMessage(
552    "label.structure_chooser_no_of_structures",
553    noOfStructuresFound, totalTime));
554    }
555    else
556    {
557  2 mainFrame.setTitle(MessageManager
558    .getString("label.structure_chooser_manual_association"));
559  2 if (errors.size() > 0)
560    {
561  2 StringBuilder errorMsg = new StringBuilder();
562  2 for (String error : errors)
563    {
564  2 errorMsg.append(error).append("\n");
565    }
566  2 if (!Jalview.isHeadlessMode())
567    {
568  2 JvOptionPane.showMessageDialog(this, errorMsg.toString(),
569    MessageManager.getString("label.pdb_web-service_error"),
570    JvOptionPane.ERROR_MESSAGE);
571    }
572    else
573    {
574  0 Console.error(
575    MessageManager.getString("label.pdb_web-service_error"));
576  0 Console.debug(errorMsg.toString());
577    }
578    }
579    }
580    }
581   
 
582  3 toggle protected void loadLocalCachedPDBEntries()
583    {
584  3 ArrayList<CachedPDB> entries = new ArrayList<>();
585  3 for (SequenceI seq : selectedSequences)
586    {
587  3 if (seq.getDatasetSequence() != null
588    && seq.getDatasetSequence().getAllPDBEntries() != null)
589    {
590  3 for (PDBEntry pdbEntry : seq.getDatasetSequence()
591    .getAllPDBEntries())
592    {
593  0 if (pdbEntry.getFile() != null)
594    {
595  0 entries.add(new CachedPDB(seq, pdbEntry));
596    }
597    }
598    }
599    }
600  3 cachedPDBExists = !entries.isEmpty();
601  3 PDBEntryTableModel tableModelx = new PDBEntryTableModel(entries);
602  3 tbl_local_pdb.setModel(tableModelx);
603    }
604   
605    /**
606    * Filters a given list of discovered structures based on supplied argument
607    *
608    * @param fieldToFilterBy
609    * the field to filter by
610    */
 
611  1 toggle void filterResultSet(final String fieldToFilterBy)
612    {
613  1 Thread filterThread = new Thread(new Runnable()
614    {
615   
 
616  1 toggle @Override
617    public void run()
618    {
619  1 long startTime = System.currentTimeMillis();
620  1 lbl_loading.setVisible(true);
621  1 Collection<FTSDataColumnI> wantedFields = data.getDocFieldPrefs()
622    .getStructureSummaryFields();
623  1 Collection<FTSData> filteredResponse = new HashSet<>();
624  1 HashSet<String> errors = new HashSet<>();
625   
626  1 for (SequenceI seq : selectedSequences)
627    {
628   
629  1 FTSRestResponse resultList;
630  1 try
631    {
632  1 resultList = data.selectFirstRankedQuery(seq,
633    discoveredStructuresSet, wantedFields, fieldToFilterBy,
634    !chk_invertFilter.isSelected());
635   
636    } catch (Exception e)
637    {
638  0 Console.debugPrintStackTrace(e);
639  0 errors.add(e.getMessage());
640  0 continue;
641    }
642  1 if (resultList.getSearchSummary() != null
643    && !resultList.getSearchSummary().isEmpty())
644    {
645  0 filteredResponse.addAll(resultList.getSearchSummary());
646    }
647    }
648   
649  1 String totalTime = (System.currentTimeMillis() - startTime)
650    + " milli secs";
651  1 if (!filteredResponse.isEmpty())
652    {
653  0 final int filterResponseCount = filteredResponse.size();
654  0 Collection<FTSData> reorderedStructuresSet = new LinkedHashSet<>();
655  0 reorderedStructuresSet.addAll(filteredResponse);
656  0 reorderedStructuresSet.addAll(discoveredStructuresSet);
657  0 getResultTable()
658    .setModel(data.getTableModel(reorderedStructuresSet));
659   
660  0 FTSRestResponse.configureTableColumn(getResultTable(),
661    wantedFields, tempUserPrefs);
662  0 getResultTable().getColumn("Ref Sequence").setPreferredWidth(120);
663  0 getResultTable().getColumn("Ref Sequence").setMinWidth(100);
664  0 getResultTable().getColumn("Ref Sequence").setMaxWidth(200);
665    // Update table selection model here
666  0 getResultTable().addRowSelectionInterval(0,
667    filterResponseCount - 1);
668  0 mainFrame.setTitle(MessageManager.formatMessage(
669    "label.structure_chooser_filter_time", totalTime));
670    }
671    else
672    {
673  1 mainFrame.setTitle(MessageManager.formatMessage(
674    "label.structure_chooser_filter_time", totalTime));
675  1 if (errors.size() > 0)
676    {
677  0 StringBuilder errorMsg = new StringBuilder();
678  0 for (String error : errors)
679    {
680  0 errorMsg.append(error).append("\n");
681    }
682  0 JvOptionPane.showMessageDialog(null, errorMsg.toString(),
683    MessageManager.getString("label.pdb_web-service_error"),
684    JvOptionPane.ERROR_MESSAGE);
685    }
686    }
687   
688  1 lbl_loading.setVisible(false);
689   
690  1 validateSelections();
691    }
692    });
693  1 filterThread.start();
694    }
695   
696    /**
697    * Handles action event for btn_pdbFromFile
698    */
 
699  0 toggle @Override
700    protected void pdbFromFile_actionPerformed()
701    {
702    // TODO: JAL-3048 not needed for Jalview-JS until JSmol dep and
703    // StructureChooser
704    // works
705  0 JalviewFileChooser chooser = new JalviewFileChooser(
706    Cache.getProperty("LAST_DIRECTORY"));
707  0 chooser.setFileView(new JalviewFileView());
708  0 chooser.setDialogTitle(
709    MessageManager.formatMessage("label.select_pdb_file_for",
710    selectedSequence.getDisplayId(false)));
711  0 chooser.setToolTipText(MessageManager.formatMessage(
712    "label.load_pdb_file_associate_with_sequence",
713    selectedSequence.getDisplayId(false)));
714   
715  0 int value = chooser.showOpenDialog(null);
716  0 if (value == JalviewFileChooser.APPROVE_OPTION)
717    {
718  0 selectedPdbFileName = chooser.getSelectedFile().getPath();
719  0 Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
720  0 boolean guessTFType = localPdbPaeMatrixFileName == null;
721  0 localPdbPaeMatrixFileName = guessPAEFilename();
722  0 guessTFType |= localPdbPaeMatrixFileName != null;
723  0 Regex alphaFold = JmolParser.getNewAlphafoldValidator();
724  0 if (guessTFType
725    && alphaFold.search(new File(selectedPdbFileName).getName())
726    && !tempFacAsChanged)
727    {
728    // localPdbPaeMatrixFileName was null and now isn't and filename could
729    // well be AlphaFold and user hasn't adjusted the tempFacType
730  0 combo_tempFacAs.setSelectedItem(TFType.PLDDT);
731    }
732  0 validateSelections();
733    }
734    }
735   
736    /**
737    * Handles action event for btn_paeMatrixFile
738    */
 
739  0 toggle @Override
740    protected void paeMatrixFile_actionPerformed()
741    {
742  0 File pdbFile = new File(selectedPdbFileName);
743  0 String setFile = Cache.getProperty("LAST_DIRECTORY");
744  0 if (localPdbPaeMatrixFileName != null)
745    {
746  0 File paeFile = new File(localPdbPaeMatrixFileName);
747  0 if (paeFile.exists())
748  0 setFile = paeFile.getAbsolutePath();
749  0 else if (paeFile.getParentFile().exists())
750  0 setFile = paeFile.getParentFile().getAbsolutePath();
751    }
752    else
753    {
754  0 String guess = guessPAEFilename();
755  0 if (guess != null)
756  0 setFile = guess;
757    }
758  0 JalviewFileChooser chooser = new JalviewFileChooser(setFile);
759  0 chooser.setFileView(new JalviewFileView());
760  0 chooser.setDialogTitle(MessageManager.formatMessage(
761    "label.select_pae_matrix_file_for", pdbFile.getName()));
762  0 chooser.setToolTipText(MessageManager.formatMessage(
763    "label.load_pae_matrix_file_associate_with_structure",
764    pdbFile.getName()));
765   
766    // TODO convert to Callable/Promise
767  0 int value = chooser.showOpenDialog(null);
768  0 if (value == JalviewFileChooser.APPROVE_OPTION)
769    {
770  0 String fileName = chooser.getSelectedFile().getPath();
771  0 try
772    {
773  0 PAEContactMatrix.validateContactMatrixFile(fileName);
774    } catch (Exception thr)
775    {
776  0 JvOptionPane.showInternalMessageDialog(this, MessageManager
777    .formatMessage("label.couldnt_load_file", new Object[]
778    { fileName }) + "<br>" + thr.getLocalizedMessage(),
779    MessageManager.getString("label.error_loading_file"),
780    JvOptionPane.WARNING_MESSAGE);
781  0 Console.error("Couldn't import " + fileName + " as a PAE matrix",
782    thr);
783  0 return;
784    }
785  0 localPdbPaeMatrixFileName = fileName;
786  0 Cache.setProperty("LAST_DIRECTORY", localPdbPaeMatrixFileName);
787    }
788  0 validateAssociationFromFile();
789    }
790   
 
791  0 toggle private String guessPAEFilename()
792    {
793  0 if (selectedPdbFileName.toLowerCase(Locale.ROOT).endsWith(".pdb")
794    || selectedPdbFileName.toLowerCase(Locale.ROOT)
795    .endsWith(".cif"))
796    {
797  0 String jsonExt = selectedPdbFileName.substring(0,
798    selectedPdbFileName.length() - 4) + ".json";
799    // AlphaFold naming scheme
800  0 String guessFile1 = StringUtils.replaceLast(jsonExt, "model",
801    "predicted_aligned_error");
802    // nf-core mode naming scheme
803  0 String guessFile2 = StringUtils.replaceLast(jsonExt, ".json",
804    "_scores.json");
805  0 if (new File(guessFile1).exists())
806    {
807  0 return guessFile1;
808    }
809  0 else if (new File(jsonExt).exists())
810    {
811  0 return jsonExt;
812    }
813  0 else if (new File(guessFile2).exists())
814    {
815  0 return guessFile2;
816    }
817    }
818  0 return null;
819    }
820   
821    /**
822    * Populates the filter combo-box options dynamically depending on discovered
823    * structures
824    */
 
825  9 toggle protected void populateFilterComboBox(boolean haveData,
826    boolean cachedPDBExist)
827    {
828  9 populateFilterComboBox(haveData, cachedPDBExist, null);
829    }
830   
831    /**
832    * Populates the filter combo-box options dynamically depending on discovered
833    * structures
834    */
 
835  9 toggle protected void populateFilterComboBox(boolean haveData,
836    boolean cachedPDBExist, FilterOption lastSel)
837    {
838   
839    /*
840    * temporarily suspend the change listener behaviour
841    */
842  9 cmb_filterOption.removeItemListener(this);
843  9 int selSet = -1;
844  9 cmb_filterOption.removeAllItems();
845  9 if (haveData)
846    {
847  6 List<FilterOption> filters = data
848    .getAvailableFilterOptions(VIEWS_FILTER);
849  6 data.updateAvailableFilterOptions(VIEWS_FILTER, filters,
850    lastDiscoveredStructuresSet);
851  6 int p = 0;
852  6 for (FilterOption filter : filters)
853    {
854  39 if (lastSel != null && filter.equals(lastSel))
855    {
856  0 selSet = p;
857    }
858  39 p++;
859  39 cmb_filterOption.addItem(filter);
860    }
861    }
862   
863  9 cmb_filterOption.addItem(
864    new FilterOption(MessageManager.getString("label.enter_pdb_id"),
865    "-", VIEWS_ENTER_ID, false, null));
866  9 cmb_filterOption.addItem(
867    new FilterOption(MessageManager.getString("label.from_file"),
868    "-", VIEWS_FROM_FILE, false, null));
869  9 if (canQueryTDB && notQueriedTDBYet)
870    {
871  2 btn_queryTDB.setVisible(true);
872  2 pnl_queryTDB.setVisible(true);
873    }
874   
875  9 if (cachedPDBExist)
876    {
877  1 FilterOption cachedOption = new FilterOption(
878    MessageManager.getString("label.cached_structures"), "-",
879    VIEWS_LOCAL_PDB, false, null);
880  1 cmb_filterOption.addItem(cachedOption);
881  1 if (selSet == -1)
882    {
883  1 cmb_filterOption.setSelectedItem(cachedOption);
884    }
885    }
886  9 if (selSet > -1)
887    {
888  0 cmb_filterOption.setSelectedIndex(selSet);
889    }
890  9 cmb_filterOption.addItemListener(this);
891    }
892   
893    /**
894    * Updates the displayed view based on the selected filter option
895    */
 
896  2 toggle protected void updateCurrentView()
897    {
898  2 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
899    .getSelectedItem());
900   
901  2 if (lastSelected == selectedFilterOpt)
902    {
903    // don't need to do anything, probably
904  0 return;
905    }
906    // otherwise, record selection
907    // and update the layout and dialog accordingly
908  2 lastSelected = selectedFilterOpt;
909   
910  2 layout_switchableViews.show(pnl_switchableViews,
911    selectedFilterOpt.getView());
912  2 String filterTitle = mainFrame.getTitle();
913  2 mainFrame.setTitle(frameTitle);
914  2 chk_invertFilter.setVisible(false);
915   
916  2 if (selectedFilterOpt.getView() == VIEWS_FILTER)
917    {
918  1 mainFrame.setTitle(filterTitle);
919    // TDB Query has no invert as yet
920  1 chk_invertFilter.setVisible(selectedFilterOpt
921    .getQuerySource() instanceof PDBStructureChooserQuerySource);
922   
923  1 if (data != selectedFilterOpt.getQuerySource()
924    || data.needsRefetch(selectedFilterOpt))
925    {
926  0 data = selectedFilterOpt.getQuerySource();
927    // rebuild the views completely, since prefs will also change
928  0 tabRefresh();
929  0 return;
930    }
931    else
932    {
933  1 filterResultSet(selectedFilterOpt.getValue());
934    }
935    }
936  1 else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
937    || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
938    {
939  1 mainFrame.setTitle(MessageManager
940    .getString("label.structure_chooser_manual_association"));
941  1 idInputAssSeqPanel.loadCmbAssSeq();
942  1 fileChooserAssSeqPanel.loadCmbAssSeq();
943    }
944  2 validateSelections();
945    }
946   
947    /**
948    * Validates user selection and enables the 'Add' and 'New View' buttons if
949    * all parameters are correct (the Add button will only be visible if there is
950    * at least one existing structure viewer open). This basically means at least
951    * one structure selected and no error messages.
952    * <p>
953    * The 'Superpose Structures' option is enabled if either more than one
954    * structure is selected, or the 'Add' to existing view option is enabled, and
955    * disabled if the only option is to open a new view of a single structure.
956    */
 
957  5 toggle @Override
958    protected void validateSelections()
959    {
960  5 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
961    .getSelectedItem());
962  5 btn_add.setEnabled(false);
963  5 String currentView = selectedFilterOpt.getView();
964  5 int selectedCount = 0;
965  5 if (currentView == VIEWS_FILTER)
966    {
967  2 selectedCount = getResultTable().getSelectedRows().length;
968  2 if (selectedCount > 0)
969    {
970  0 btn_add.setEnabled(true);
971    }
972    }
973  3 else if (currentView == VIEWS_LOCAL_PDB)
974    {
975  0 selectedCount = tbl_local_pdb.getSelectedRows().length;
976  0 if (selectedCount > 0)
977    {
978  0 btn_add.setEnabled(true);
979    }
980    }
981  3 else if (currentView == VIEWS_ENTER_ID)
982    {
983  3 validateAssociationEnterPdb();
984    }
985  0 else if (currentView == VIEWS_FROM_FILE)
986    {
987  0 validateAssociationFromFile();
988    }
989   
990  5 btn_newView.setEnabled(btn_add.isEnabled());
991   
992    /*
993    * enable 'Superpose' option if more than one structure is selected,
994    * or there are view(s) available to add structure(s) to
995    */
996  5 chk_superpose
997    .setEnabled(selectedCount > 1 || targetView.getItemCount() > 0);
998    }
999   
 
1000  0 toggle @Override
1001    protected boolean showPopupFor(int selectedRow, int x, int y)
1002    {
1003  0 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
1004    .getSelectedItem());
1005  0 String currentView = selectedFilterOpt.getView();
1006   
1007  0 if (currentView == VIEWS_FILTER
1008    && data instanceof ThreeDBStructureChooserQuerySource)
1009    {
1010   
1011  0 TDB_FTSData row = ((ThreeDBStructureChooserQuerySource) data)
1012    .getFTSDataFor(getResultTable(), selectedRow,
1013    discoveredStructuresSet);
1014  0 String pageUrl = row.getModelViewUrl();
1015   
1016  0 JPopupMenu popup = new JPopupMenu("3D Beacons");
1017  0 JMenuItem viewUrl = new JMenuItem("View model web page");
1018  0 if (pageUrl == null || "".equals(pageUrl.trim()))
1019    {
1020  0 viewUrl.setEnabled(false);
1021  0 viewUrl.setText("No model page available.");
1022    }
1023  0 viewUrl.addActionListener(new ActionListener()
1024    {
 
1025  0 toggle @Override
1026    public void actionPerformed(ActionEvent e)
1027    {
1028  0 Desktop.showUrl(pageUrl);
1029    }
1030    });
1031  0 popup.add(viewUrl);
1032  0 SwingUtilities.invokeLater(new Runnable()
1033    {
 
1034  0 toggle @Override
1035    public void run()
1036    {
1037  0 popup.show(getResultTable(), x, y);
1038    }
1039    });
1040  0 return true;
1041    }
1042    // event not handled by us
1043  0 return false;
1044    }
1045   
1046    /**
1047    * Validates inputs from the Manual PDB entry panel
1048    */
 
1049  3 toggle protected void validateAssociationEnterPdb()
1050    {
1051  3 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
1052    .getCmb_assSeq().getSelectedItem();
1053  3 lbl_pdbManualFetchStatus.setIcon(errorImage);
1054  3 lbl_pdbManualFetchStatus.setToolTipText("");
1055  3 if (txt_search.getText().length() > 0)
1056    {
1057  0 lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(true,
1058    MessageManager.formatMessage("info.no_pdb_entry_found_for",
1059    txt_search.getText())));
1060    }
1061   
1062  3 if (errorWarning.length() > 0)
1063    {
1064  0 lbl_pdbManualFetchStatus.setIcon(warningImage);
1065  0 lbl_pdbManualFetchStatus.setToolTipText(
1066    JvSwingUtils.wrapTooltip(true, errorWarning.toString()));
1067    }
1068   
1069  3 if (selectedSequences.length == 1 || !assSeqOpt.getName()
1070    .equalsIgnoreCase("-Select Associated Seq-"))
1071    {
1072  3 txt_search.setEnabled(true);
1073  3 if (isValidPBDEntry)
1074    {
1075  0 btn_add.setEnabled(true);
1076  0 lbl_pdbManualFetchStatus.setToolTipText("");
1077  0 lbl_pdbManualFetchStatus.setIcon(goodImage);
1078    }
1079    }
1080    else
1081    {
1082  0 txt_search.setEnabled(false);
1083  0 lbl_pdbManualFetchStatus.setIcon(errorImage);
1084    }
1085    }
1086   
1087    /**
1088    * Validates inputs for the manual PDB file selection options
1089    */
 
1090  0 toggle protected void validateAssociationFromFile()
1091    {
1092  0 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
1093    .getCmb_assSeq().getSelectedItem();
1094    // lbl_fromFileStatus.setIcon(errorImage);
1095  0 String pdbFileString = "";
1096  0 String pdbFileTooltip = "";
1097  0 if (selectedSequences.length == 1 || (assSeqOpt != null && !assSeqOpt
1098    .getName().equalsIgnoreCase("-Select Associated Seq-")))
1099    {
1100  0 btn_pdbFromFile.setEnabled(true);
1101  0 if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
1102    {
1103  0 btn_add.setEnabled(true);
1104    // lbl_fromFileStatus.setIcon(goodImage);
1105  0 pdbFileString = new File(selectedPdbFileName).getName();
1106  0 pdbFileTooltip = new File(selectedPdbFileName).getAbsolutePath();
1107  0 setPdbOptionsEnabled(true);
1108    }
1109    else
1110    {
1111  0 pdbFileString = MessageManager.getString("label.none");
1112  0 pdbFileTooltip = MessageManager.getString("label.nothing_selected");
1113  0 setPdbOptionsEnabled(false);
1114    }
1115    }
1116    else
1117    {
1118  0 btn_pdbFromFile.setEnabled(false);
1119  0 setPdbOptionsEnabled(false);
1120    // lbl_fromFileStatus.setIcon(errorImage);
1121  0 pdbFileString = MessageManager.getString("label.none");
1122  0 pdbFileTooltip = MessageManager.getString("label.nothing_selected");
1123    }
1124  0 lbl_pdbFile.setText(pdbFileString);
1125  0 lbl_pdbFile.setToolTipText(pdbFileTooltip);
1126   
1127    // PAE file choice
1128  0 String paeFileString = "";
1129  0 String paeFileTooltip = "";
1130  0 if (localPdbPaeMatrixFileName != null
1131    && localPdbPaeMatrixFileName.length() > 0)
1132    {
1133  0 paeFileString = new File(localPdbPaeMatrixFileName).getName();
1134  0 paeFileTooltip = new File(localPdbPaeMatrixFileName)
1135    .getAbsolutePath();
1136    }
1137    else
1138    {
1139  0 paeFileString = MessageManager.getString("label.none");
1140  0 paeFileTooltip = MessageManager.getString("label.nothing_selected");
1141    }
1142  0 lbl_paeFile.setText(paeFileString);
1143  0 lbl_paeFile.setToolTipText(paeFileTooltip);
1144    }
1145   
 
1146  2 toggle @Override
1147    protected void cmbAssSeqStateChanged()
1148    {
1149  2 validateSelections();
1150    }
1151   
1152    private FilterOption lastSelected = null;
1153   
1154    /**
1155    * Handles the state change event for the 'filter' combo-box and 'invert'
1156    * check-box
1157    */
 
1158  0 toggle @Override
1159    protected void stateChanged(ItemEvent e)
1160    {
1161  0 if (e.getSource() instanceof JCheckBox)
1162    {
1163  0 updateCurrentView();
1164    }
1165    else
1166    {
1167  0 if (e.getStateChange() == ItemEvent.SELECTED)
1168    {
1169  0 updateCurrentView();
1170    }
1171    }
1172   
1173    }
1174   
1175    /**
1176    * select structures for viewing by their PDB IDs
1177    *
1178    * @param pdbids
1179    * @return true if structures were found and marked as selected
1180    */
 
1181  0 toggle public boolean selectStructure(String... pdbids)
1182    {
1183  0 boolean found = false;
1184   
1185  0 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
1186    .getSelectedItem());
1187  0 String currentView = selectedFilterOpt.getView();
1188  0 JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
1189  0 : (currentView == VIEWS_LOCAL_PDB) ? tbl_local_pdb : null;
1190   
1191  0 if (restable == null)
1192    {
1193    // can't select (enter PDB ID, or load file - need to also select which
1194    // sequence to associate with)
1195  0 return false;
1196    }
1197   
1198  0 int pdbIdColIndex = restable.getColumn("PDB Id").getModelIndex();
1199  0 for (int r = 0; r < restable.getRowCount(); r++)
1200    {
1201  0 for (int p = 0; p < pdbids.length; p++)
1202    {
1203  0 if (String.valueOf(restable.getValueAt(r, pdbIdColIndex))
1204    .equalsIgnoreCase(pdbids[p]))
1205    {
1206  0 restable.setRowSelectionInterval(r, r);
1207  0 found = true;
1208    }
1209    }
1210    }
1211  0 return found;
1212    }
1213   
1214    /**
1215    * Handles the 'New View' action
1216    */
 
1217  0 toggle @Override
1218    protected void newView_ActionPerformed()
1219    {
1220  0 targetView.setSelectedItem(null);
1221  0 showStructures(false);
1222    }
1223   
1224    /**
1225    * Handles the 'Add to existing viewer' action
1226    */
 
1227  0 toggle @Override
1228    protected void add_ActionPerformed()
1229    {
1230  0 showStructures(false);
1231    }
1232   
1233    /**
1234    * structure viewer opened by this dialog, or null
1235    */
1236    private StructureViewer sViewer = null;
1237   
 
1238  0 toggle public void showStructures(boolean waitUntilFinished)
1239    {
1240   
1241  0 final StructureSelectionManager ssm = ap.getStructureSelectionManager();
1242   
1243  0 final int preferredHeight = pnl_filter.getHeight();
1244  0 btn_add.setEnabled(false);
1245  0 btn_newView.setEnabled(false);
1246  0 btn_cancel.setEnabled(false);
1247  0 actionsPanel.setEnabled(false);
1248   
1249  0 final String progress = MessageManager
1250    .getString("label.working_ellipsis");
1251  0 setProgressBar(progress, progress.hashCode());
1252  0 Runnable viewStruc = new Runnable()
1253    {
 
1254  0 toggle @Override
1255    public void run()
1256    {
1257  0 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
1258    .getSelectedItem());
1259  0 String currentView = selectedFilterOpt.getView();
1260  0 JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
1261    : tbl_local_pdb;
1262   
1263  0 if (currentView == VIEWS_FILTER)
1264    {
1265  0 int[] selectedRows = restable.getSelectedRows();
1266  0 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
1267  0 List<SequenceI> selectedSeqsToView = new ArrayList<>();
1268  0 pdbEntriesToView = data.collectSelectedRows(restable,
1269    selectedRows, selectedSeqsToView);
1270   
1271  0 SequenceI[] selectedSeqs = selectedSeqsToView
1272    .toArray(new SequenceI[selectedSeqsToView.size()]);
1273  0 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
1274    selectedSeqs);
1275    }
1276  0 else if (currentView == VIEWS_LOCAL_PDB)
1277    {
1278  0 int[] selectedRows = tbl_local_pdb.getSelectedRows();
1279  0 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
1280  0 int count = 0;
1281  0 int pdbIdColIndex = tbl_local_pdb.getColumn("PDB Id")
1282    .getModelIndex();
1283  0 int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
1284    .getModelIndex();
1285  0 List<SequenceI> selectedSeqsToView = new ArrayList<>();
1286  0 for (int row : selectedRows)
1287    {
1288  0 PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb
1289    .getModel()).getPDBEntryAt(row).getPdbEntry();
1290   
1291  0 pdbEntriesToView[count++] = pdbEntry;
1292  0 SequenceI selectedSeq = (SequenceI) tbl_local_pdb
1293    .getValueAt(row, refSeqColIndex);
1294  0 selectedSeqsToView.add(selectedSeq);
1295    }
1296  0 SequenceI[] selectedSeqs = selectedSeqsToView
1297    .toArray(new SequenceI[selectedSeqsToView.size()]);
1298  0 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
1299    selectedSeqs);
1300    }
1301  0 else if (currentView == VIEWS_ENTER_ID)
1302    {
1303  0 SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
1304    .getCmb_assSeq().getSelectedItem()).getSequence();
1305  0 if (userSelectedSeq != null)
1306    {
1307  0 selectedSequence = userSelectedSeq;
1308    }
1309  0 String pdbIdStr = txt_search.getText();
1310  0 PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
1311  0 if (pdbEntry == null)
1312    {
1313  0 pdbEntry = new PDBEntry();
1314  0 if (pdbIdStr.split(":").length > 1)
1315    {
1316  0 pdbEntry.setId(pdbIdStr.split(":")[0]);
1317  0 pdbEntry.setChainCode(
1318    pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
1319    }
1320    else
1321    {
1322  0 pdbEntry.setId(pdbIdStr);
1323    }
1324  0 pdbEntry.setType(PDBEntry.Type.PDB);
1325  0 selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
1326    }
1327   
1328  0 PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
1329  0 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
1330    new SequenceI[]
1331    { selectedSequence });
1332    }
1333  0 else if (currentView == VIEWS_FROM_FILE)
1334    {
1335  0 StructureChooser sc = StructureChooser.this;
1336  0 TFType tft = (TFType) sc.combo_tempFacAs.getSelectedItem();
1337  0 String paeFilename = sc.localPdbPaeMatrixFileName;
1338  0 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
1339    .getCmb_assSeq().getSelectedItem();
1340  0 SequenceI userSelectedSeq = assSeqOpt.getSequence();
1341  0 if (userSelectedSeq != null)
1342    {
1343  0 selectedSequence = userSelectedSeq;
1344    }
1345  0 String pdbFilename = selectedPdbFileName;
1346    // TODO - tidy up this ugly hack so we call launchStructureViewer too
1347  0 StructureChooser.openStructureFileForSequence(ssm, sc, ap,
1348    selectedSequence, true, pdbFilename, tft, paeFilename,
1349    false, true, false,
1350    getTargetedStructureViewer(ssm).getViewerType());
1351    }
1352  0 SwingUtilities.invokeLater(new Runnable()
1353    {
 
1354  0 toggle @Override
1355    public void run()
1356    {
1357  0 setProgressBar("Complete.", progress.hashCode());
1358  0 closeAction(preferredHeight);
1359  0 mainFrame.dispose();
1360    }
1361    });
1362    }
1363    };
1364  0 Thread runner = new Thread(viewStruc);
1365  0 runner.start();
1366  0 if (waitUntilFinished)
1367    {
1368  0 while (sViewer == null ? runner.isAlive()
1369  0 : (sViewer.sview == null ? true
1370    : !sViewer.sview.hasMapping()))
1371    {
1372  0 try
1373    {
1374  0 Thread.sleep(300);
1375    } catch (InterruptedException ie)
1376    {
1377   
1378    }
1379    }
1380    }
1381    }
1382   
1383    /**
1384    * Answers a structure viewer (new or existing) configured to superimpose
1385    * added structures or not according to the user's choice
1386    *
1387    * @param ssm
1388    * @return
1389    */
 
1390  36 toggle StructureViewer getTargetedStructureViewer(StructureSelectionManager ssm)
1391    {
1392  36 Object sv = targetView.getSelectedItem();
1393   
1394  36 return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv;
1395    }
1396   
1397    /**
1398    * Adds PDB structures to a new or existing structure viewer
1399    *
1400    * @param ssm
1401    * @param pdbEntriesToView
1402    * @param alignPanel
1403    * @param sequences
1404    * @return
1405    */
 
1406  0 toggle private StructureViewer launchStructureViewer(
1407    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1408    final AlignmentPanel alignPanel, SequenceI[] sequences)
1409    {
1410  0 return launchStructureViewer(ssm, pdbEntriesToView, alignPanel,
1411    sequences, null);
1412    }
1413   
 
1414  36 toggle private StructureViewer launchStructureViewer(
1415    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1416    final AlignmentPanel alignPanel, SequenceI[] sequences,
1417    ViewerType viewerType)
1418    {
1419  36 long progressId = sequences.hashCode();
1420  36 setProgressBar(MessageManager
1421    .getString("status.launching_3d_structure_viewer"), progressId);
1422  36 final StructureViewer theViewer = getTargetedStructureViewer(ssm);
1423  36 boolean superimpose = chk_superpose.isSelected();
1424  36 theViewer.setSuperpose(superimpose);
1425   
1426    // if we're running in --headless mode make this viewer synchronous
1427  36 if (Jalview.isHeadlessMode())
1428    {
1429  19 theViewer.setAsync(false);
1430    }
1431   
1432    /*
1433    * remember user's choice of superimpose or not
1434    */
1435  36 Cache.setProperty(AUTOSUPERIMPOSE,
1436    Boolean.valueOf(superimpose).toString());
1437   
1438  36 setProgressBar(null, progressId);
1439  36 if (SiftsSettings.isMapWithSifts())
1440    {
1441  0 List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
1442  0 int p = 0;
1443    // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
1444    // real PDB ID. For moment, we can also safely do this if there is already
1445    // a known mapping between the PDBEntry and the sequence.
1446  0 for (SequenceI seq : sequences)
1447    {
1448  0 PDBEntry pdbe = pdbEntriesToView[p++];
1449  0 if (pdbe != null && pdbe.getFile() != null)
1450    {
1451  0 StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
1452  0 if (smm != null && smm.length > 0)
1453    {
1454  0 for (StructureMapping sm : smm)
1455    {
1456  0 if (sm.getSequence() == seq)
1457    {
1458  0 continue;
1459    }
1460    }
1461    }
1462    }
1463  0 if (seq.getPrimaryDBRefs().isEmpty())
1464    {
1465  0 seqsWithoutSourceDBRef.add(seq);
1466  0 continue;
1467    }
1468    }
1469  0 if (!seqsWithoutSourceDBRef.isEmpty())
1470    {
1471  0 int y = seqsWithoutSourceDBRef.size();
1472  0 setProgressBar(MessageManager.formatMessage(
1473    "status.fetching_dbrefs_for_sequences_without_valid_refs",
1474    y), progressId);
1475  0 SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
1476    .toArray(new SequenceI[y]);
1477  0 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
1478  0 dbRefFetcher.fetchDBRefs(true);
1479   
1480  0 setProgressBar("Fetch complete.", progressId); // todo i18n
1481    }
1482    }
1483  36 if (pdbEntriesToView.length > 1)
1484    {
1485  0 setProgressBar(
1486    MessageManager.getString(
1487    "status.fetching_3d_structures_for_selected_entries"),
1488    progressId);
1489  0 theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel,
1490    viewerType);
1491    }
1492    else
1493    {
1494  36 setProgressBar(MessageManager.formatMessage(
1495    "status.fetching_3d_structures_for",
1496    pdbEntriesToView[0].getId()), progressId);
1497    // Can we pass a pre-computeMappinged pdbFile?
1498  36 theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel,
1499    viewerType);
1500    }
1501  36 setProgressBar(null, progressId);
1502    // remember the last viewer we used...
1503  36 lastTargetedView = theViewer;
1504  36 return theViewer;
1505    }
1506   
1507    /**
1508    * Populates the combo-box used in associating manually fetched structures to
1509    * a unique sequence when more than one sequence selection is made.
1510    */
 
1511  2 toggle @Override
1512    protected void populateCmbAssociateSeqOptions(
1513    JComboBox<AssociateSeqOptions> cmb_assSeq,
1514    JLabel lbl_associateSeq)
1515    {
1516  2 cmb_assSeq.removeAllItems();
1517  2 cmb_assSeq.addItem(
1518    new AssociateSeqOptions("-Select Associated Seq-", null));
1519  2 lbl_associateSeq.setVisible(false);
1520  2 if (selectedSequences.length > 1)
1521    {
1522  0 for (SequenceI seq : selectedSequences)
1523    {
1524  0 cmb_assSeq.addItem(new AssociateSeqOptions(seq));
1525    }
1526    }
1527    else
1528    {
1529  2 String seqName = selectedSequence.getDisplayId(false);
1530  2 seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
1531  2 lbl_associateSeq.setText(seqName);
1532  2 lbl_associateSeq.setVisible(true);
1533  2 cmb_assSeq.setVisible(false);
1534    }
1535    }
1536   
 
1537  3 toggle protected boolean isStructuresDiscovered()
1538    {
1539  3 return discoveredStructuresSet != null
1540    && !discoveredStructuresSet.isEmpty();
1541    }
1542   
1543    protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes
1544    // this.
1545    // Doing a search for "1" or "1c" is valuable?
1546    // Those work but are enormously slow.
1547   
 
1548  0 toggle @Override
1549    protected void txt_search_ActionPerformed()
1550    {
1551  0 String text = txt_search.getText().trim();
1552  0 if (text.length() >= PDB_ID_MIN)
1553  0 new Thread()
1554    {
1555   
 
1556  0 toggle @Override
1557    public void run()
1558    {
1559  0 errorWarning.setLength(0);
1560  0 isValidPBDEntry = false;
1561  0 if (text.length() > 0)
1562    {
1563    // TODO move this pdb id search into the PDB specific
1564    // FTSSearchEngine
1565    // for moment, it will work fine as is because it is self-contained
1566  0 String searchTerm = text.toLowerCase(Locale.ROOT);
1567  0 searchTerm = searchTerm.split(":")[0];
1568    // jalview.bin.Console.outPrintln(">>>>> search term : " +
1569    // searchTerm);
1570  0 List<FTSDataColumnI> wantedFields = new ArrayList<>();
1571  0 FTSRestRequest pdbRequest = new FTSRestRequest();
1572  0 pdbRequest.setAllowEmptySeq(false);
1573  0 pdbRequest.setResponseSize(1);
1574  0 pdbRequest.setFieldToSearchBy("(pdb_id:");
1575  0 pdbRequest.setWantedFields(wantedFields);
1576  0 pdbRequest.setSearchTerm(searchTerm + ")");
1577  0 pdbRequest.setAssociatedSequence(selectedSequence);
1578  0 FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance();
1579  0 wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
1580  0 FTSRestResponse resultList;
1581  0 try
1582    {
1583  0 resultList = pdbRestClient.executeRequest(pdbRequest);
1584    } catch (Exception e)
1585    {
1586  0 errorWarning.append(e.getMessage());
1587  0 return;
1588    } finally
1589    {
1590  0 validateSelections();
1591    }
1592  0 if (resultList.getSearchSummary() != null
1593    && resultList.getSearchSummary().size() > 0)
1594    {
1595  0 isValidPBDEntry = true;
1596    }
1597    }
1598  0 validateSelections();
1599    }
1600    }.start();
1601    }
1602   
 
1603  55 toggle @Override
1604    protected void tabRefresh()
1605    {
1606  55 if (selectedSequences != null)
1607    {
1608  0 lbl_loading.setVisible(true);
1609  0 Thread refreshThread = new Thread(new Runnable()
1610    {
 
1611  0 toggle @Override
1612    public void run()
1613    {
1614  0 fetchStructuresMetaData();
1615    // populateFilterComboBox(true, cachedPDBExists);
1616   
1617  0 filterResultSet(
1618    ((FilterOption) cmb_filterOption.getSelectedItem())
1619    .getValue());
1620  0 lbl_loading.setVisible(false);
1621    }
1622    });
1623  0 refreshThread.start();
1624    }
1625    }
1626   
 
1627    public class PDBEntryTableModel extends AbstractTableModel
1628    {
1629    String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type",
1630    "File" };
1631   
1632    private List<CachedPDB> pdbEntries;
1633   
 
1634  3 toggle public PDBEntryTableModel(List<CachedPDB> pdbEntries)
1635    {
1636  3 this.pdbEntries = new ArrayList<>(pdbEntries);
1637    }
1638   
 
1639  15 toggle @Override
1640    public String getColumnName(int columnIndex)
1641    {
1642  15 return columns[columnIndex];
1643    }
1644   
 
1645  21 toggle @Override
1646    public int getRowCount()
1647    {
1648  21 return pdbEntries.size();
1649    }
1650   
 
1651  18 toggle @Override
1652    public int getColumnCount()
1653    {
1654  18 return columns.length;
1655    }
1656   
 
1657  0 toggle @Override
1658    public boolean isCellEditable(int row, int column)
1659    {
1660  0 return false;
1661    }
1662   
 
1663  0 toggle @Override
1664    public Object getValueAt(int rowIndex, int columnIndex)
1665    {
1666  0 Object value = "??";
1667  0 CachedPDB entry = pdbEntries.get(rowIndex);
1668  0 switch (columnIndex)
1669    {
1670  0 case 0:
1671  0 value = entry.getSequence();
1672  0 break;
1673  0 case 1:
1674  0 value = entry.getQualifiedId();
1675  0 break;
1676  0 case 2:
1677  0 value = entry.getPdbEntry().getChainCode() == null ? "_"
1678    : entry.getPdbEntry().getChainCode();
1679  0 break;
1680  0 case 3:
1681  0 value = entry.getPdbEntry().getType();
1682  0 break;
1683  0 case 4:
1684  0 value = entry.getPdbEntry().getFile();
1685  0 break;
1686    }
1687  0 return value;
1688    }
1689   
 
1690  0 toggle @Override
1691    public Class<?> getColumnClass(int columnIndex)
1692    {
1693  0 return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
1694    }
1695   
 
1696  0 toggle public CachedPDB getPDBEntryAt(int row)
1697    {
1698  0 return pdbEntries.get(row);
1699    }
1700   
1701    }
1702   
 
1703    private class CachedPDB
1704    {
1705    private SequenceI sequence;
1706   
1707    private PDBEntry pdbEntry;
1708   
 
1709  0 toggle public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
1710    {
1711  0 this.sequence = sequence;
1712  0 this.pdbEntry = pdbEntry;
1713    }
1714   
 
1715  0 toggle public String getQualifiedId()
1716    {
1717  0 if (pdbEntry.hasProvider())
1718    {
1719  0 return pdbEntry.getProvider() + ":" + pdbEntry.getId();
1720    }
1721  0 return pdbEntry.toString();
1722    }
1723   
 
1724  0 toggle public SequenceI getSequence()
1725    {
1726  0 return sequence;
1727    }
1728   
 
1729  0 toggle public PDBEntry getPdbEntry()
1730    {
1731  0 return pdbEntry;
1732    }
1733   
1734    }
1735   
1736    private IProgressIndicator progressBar;
1737   
 
1738  144 toggle @Override
1739    public void setProgressBar(String message, long id)
1740    {
1741  144 if (!Platform.isHeadless() && progressBar != null)
1742  68 progressBar.setProgressBar(message, id);
1743    }
1744   
 
1745  0 toggle @Override
1746    public void registerHandler(long id, IProgressIndicatorHandler handler)
1747    {
1748  0 if (progressBar != null)
1749  0 progressBar.registerHandler(id, handler);
1750    }
1751   
 
1752  0 toggle @Override
1753    public boolean operationInProgress()
1754    {
1755  0 return progressBar == null ? false : progressBar.operationInProgress();
1756    }
1757   
 
1758  0 toggle public JalviewStructureDisplayI getOpenedStructureViewer()
1759    {
1760  0 return sViewer == null ? null : sViewer.sview;
1761    }
1762   
 
1763  0 toggle @Override
1764    protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
1765    {
1766  0 data.setDocFieldPrefs(newPrefs);
1767   
1768    }
1769   
1770    /**
1771    *
1772    * @return true when all initialisation threads have finished and dialog is
1773    * visible
1774    */
 
1775  10 toggle public boolean isDialogVisible()
1776    {
1777  10 return mainFrame != null && data != null && cmb_filterOption != null
1778    && mainFrame.isVisible()
1779    && cmb_filterOption.getSelectedItem() != null;
1780    }
1781   
1782    /**
1783    *
1784    * @return true if the 3D-Beacons query button will/has been displayed
1785    */
 
1786  1 toggle public boolean isCanQueryTDB()
1787    {
1788  1 return canQueryTDB;
1789    }
1790   
 
1791  1 toggle public boolean isNotQueriedTDBYet()
1792    {
1793  1 return notQueriedTDBYet;
1794    }
1795   
1796    /**
1797    * Open a single structure file for a given sequence
1798    */
 
1799  0 toggle public static void openStructureFileForSequence(
1800    StructureSelectionManager ssm, StructureChooser sc,
1801    AlignmentPanel ap, SequenceI seq, boolean prompt,
1802    String sFilename, TFType tft, String paeFilename,
1803    boolean doXferSettings)
1804    {
1805  0 openStructureFileForSequence(ssm, sc, ap, seq, prompt, sFilename, tft,
1806    paeFilename, false, true, doXferSettings, null);
1807    }
1808   
1809    /**
1810    *
1811    * @param ssm
1812    * @param sc
1813    * @param ap
1814    * @param seq
1815    * @param prompt
1816    * @param sFilename
1817    * @param tft
1818    * @param paeFilename
1819    * @param forceHeadless
1820    * @param showRefAnnotations
1821    * @param doXferSettings
1822    * @param viewerType
1823    * - when not null means the viewer will be opened, providing
1824    * forceHeadless/headless is not true
1825    * @return
1826    */
 
1827  52 toggle public static StructureViewer openStructureFileForSequence(
1828    StructureSelectionManager ssm, StructureChooser sc,
1829    AlignmentPanel ap, SequenceI seq, boolean prompt,
1830    String sFilename, TFType tft, String paeFilename,
1831    boolean forceHeadless, boolean showRefAnnotations,
1832    boolean doXferSettings, ViewerType viewerType)
1833    {
1834  52 StructureViewer sv = null;
1835  52 boolean headless = forceHeadless;
1836  52 if (sc == null)
1837    {
1838    // headless = true;
1839  52 prompt = false;
1840    // suppress structure viewer's external service queries
1841  52 sc = new StructureChooser(new SequenceI[] { seq }, seq, ap, false,
1842    true);
1843    }
1844  52 if (ssm == null)
1845    {
1846  52 ssm = ap.getStructureSelectionManager();
1847  52 StructureSelectionManager.doConfigureStructurePrefs(ssm);
1848    }
1849   
1850  52 PDBEntry fileEntry = new AssociatePdbFileWithSeq().associatePdbWithSeq(
1851    sFilename, DataSourceType.FILE, seq, prompt, Desktop.instance,
1852    tft, paeFilename, doXferSettings);
1853   
1854    // if headless, "false" in the sc constructor above will avoid GUI behaviour
1855    // in sc.launchStructureViewer()
1856  52 if (!headless && !(viewerType == null))
1857    {
1858  36 sv = sc.launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
1859    new SequenceI[]
1860    { seq }, viewerType);
1861    // foo
1862  36 sv.getJalviewStructureDisplay().raiseViewer();
1863    }
1864   
1865  52 sc.mainFrame.dispose();
1866   
1867    // TODO should honor preferences - only show reference annotation that is
1868    // requested - JAL-4415 JAL-3124
1869  52 if (showRefAnnotations)
1870    {
1871  37 showReferenceAnnotationsForSequence(ap.alignFrame, seq);
1872    }
1873   
1874  52 return sv;
1875    }
1876   
 
1877  37 toggle public static void showReferenceAnnotationsForSequence(AlignFrame af,
1878    SequenceI sequence)
1879    {
1880  37 AlignViewport av = af.getCurrentView();
1881  37 AlignmentI al = av.getAlignment();
1882   
1883  37 List<SequenceI> forSequences = new ArrayList<>();
1884  37 forSequences.add(sequence);
1885  37 final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
1886  37 AlignmentUtils.findAddableReferenceAnnotations(forSequences, null,
1887    candidates, al);
1888  37 final SequenceGroup selectionGroup = av.getSelectionGroup();
1889  37 AlignmentUtils.addReferenceAnnotations(candidates, al, selectionGroup);
1890  37 for (AlignmentViewPanel ap : af.getAlignPanels())
1891    {
1892    // required to readjust the height and position of the PAE
1893    // annotation
1894  37 ap.adjustAnnotationHeight();
1895    }
1896   
1897    }
1898   
 
1899  0 toggle @Override
1900    public JProgressBar getProgressBar(long id)
1901    {
1902  0 return progressBar.getProgressBar(id);
1903    }
1904   
 
1905  0 toggle public String getMessage(long id)
1906    {
1907  0 return progressIndicator.getMessage(id);
1908    }
1909   
 
1910  0 toggle public void setProgressBarMessage(long id, String message)
1911    {
1912  0 progressIndicator.setProgressBarMessage(id, message);
1913    }
1914    }