Clover icon

Coverage Report

  1. Project Clover database Fri Nov 1 2024 11:46:37 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,916
1,482
223
0.36
8.51
24.33
3.05

Classes

Class Line # Actions
StructureChooser 98 588 203
0.398158839.8%
StructureChooser.PDBEntryTableModel 1629 26 15
0.2105263221.1%
StructureChooser.CachedPDB 1705 7 5
0.00%
 

Contributing tests

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