Clover icon

Coverage Report

  1. Project Clover database Thu Jun 4 2026 14:16:38 BST
  2. Package jalview.gui

File StructureChooser.java

 

Coverage histogram

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

Code metrics

244
662
78
3
2,079
1,603
245
0.37
8.49
26
3.14

Classes

Class Line # Actions
StructureChooser 100 629 225
0.407288340.7%
StructureChooser.PDBEntryTableModel 1662 26 15
0.2105263221.1%
StructureChooser.CachedPDB 1738 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.io.IOException;
29    import java.util.ArrayList;
30    import java.util.Collection;
31    import java.util.HashSet;
32    import java.util.LinkedHashMap;
33    import java.util.LinkedHashSet;
34    import java.util.List;
35    import java.util.Locale;
36    import java.util.Map;
37    import java.util.concurrent.Executors;
38   
39    import javax.swing.JCheckBox;
40    import javax.swing.JComboBox;
41    import javax.swing.JLabel;
42    import javax.swing.JMenu;
43    import javax.swing.JMenuItem;
44    import javax.swing.JPopupMenu;
45    import javax.swing.JProgressBar;
46    import javax.swing.JTable;
47    import javax.swing.SwingUtilities;
48    import javax.swing.table.AbstractTableModel;
49   
50    import com.stevesoft.pat.Regex;
51   
52    import jalview.analysis.AlignmentUtils;
53    import jalview.api.AlignmentViewPanel;
54    import jalview.api.structures.JalviewStructureDisplayI;
55    import jalview.bin.Cache;
56    import jalview.bin.Console;
57    import jalview.bin.Jalview;
58    import jalview.datamodel.AlignmentAnnotation;
59    import jalview.datamodel.AlignmentI;
60    import jalview.datamodel.PDBEntry;
61    import jalview.datamodel.SequenceGroup;
62    import jalview.datamodel.SequenceI;
63    import jalview.ext.jmol.JmolParser;
64    import jalview.fts.api.FTSData;
65    import jalview.fts.api.FTSDataColumnI;
66    import jalview.fts.api.FTSRestClientI;
67    import jalview.fts.core.FTSDataColumnPreferences;
68    import jalview.fts.core.FTSRestRequest;
69    import jalview.fts.core.FTSRestResponse;
70    import jalview.fts.service.pdb.PDBFTSRestClient;
71    import jalview.fts.service.threedbeacons.TDB_FTSData;
72    import jalview.gui.StructureViewer.ViewerType;
73    import jalview.gui.structurechooser.PDBStructureChooserQuerySource;
74    import jalview.gui.structurechooser.StructureChooserQuerySource;
75    import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource;
76    import jalview.io.DataSourceType;
77    import jalview.io.JalviewFileChooser;
78    import jalview.io.JalviewFileView;
79    import jalview.jbgui.FilterOption;
80    import jalview.jbgui.GStructureChooser;
81    import jalview.structure.StructureImportSettings.TFType;
82    import jalview.structure.StructureMapping;
83    import jalview.structure.StructureSelectionManager;
84    import jalview.util.MessageManager;
85    import jalview.util.Platform;
86    import jalview.util.StringUtils;
87    import jalview.ws.DBRefFetcher;
88    import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
89    import jalview.ws.datamodel.alphafold.PAEContactMatrix;
90    import jalview.ws.seqfetcher.DbSourceProxy;
91    import jalview.ws.sifts.SiftsSettings;
92   
93    /**
94    * Provides the behaviors for the Structure chooser Panel
95    *
96    * @author tcnofoegbu
97    *
98    */
99    @SuppressWarnings("serial")
 
100    public class StructureChooser extends GStructureChooser
101    implements IProgressIndicator
102    {
103    public static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
104   
105    /**
106    * warn user if need to fetch more than this many uniprot records at once
107    */
108    private static final int THRESHOLD_WARN_UNIPROT_FETCH_NEEDED = 20;
109    private SequenceI selectedSequence;
110   
111    private SequenceI[] selectedSequences;
112   
113    private IProgressIndicator progressIndicator;
114   
115    private Collection<FTSData> discoveredStructuresSet;
116   
117    private StructureChooserQuerySource data;
118   
 
119  57 toggle @Override
120    protected FTSDataColumnPreferences getFTSDocFieldPrefs()
121    {
122  57 return data.getDocFieldPrefs();
123    }
124   
125    private String selectedPdbFileName;
126   
127    private String localPdbPaeMatrixFileName;
128   
129    private boolean isValidPBDEntry;
130   
131    private boolean cachedPDBExists;
132   
133    private Collection<FTSData> lastDiscoveredStructuresSet;
134   
135    private boolean canQueryTDB = false;
136   
137    private boolean notQueriedTDBYet = true;
138   
139    List<SequenceI> seqsWithoutSourceDBRef = null;
140   
141    private boolean showChooserGUI = true;
142   
143    /**
144    * when true, queries to external services are supressed (no SIFTs, no PDBe,
145    * no 3D-Beacons, etc)
146    */
147    private boolean dontQueryServices = false;
148   
149    protected static StructureViewer lastTargetedView = null;
150   
 
151  3 toggle public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
152    AlignmentPanel ap)
153    {
154  3 this(selectedSeqs, selectedSeq, ap, true);
155    }
156   
 
157  3 toggle public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
158    AlignmentPanel ap, boolean showGUI)
159    {
160  3 this(selectedSeqs, selectedSeq, ap, showGUI, false);
161    }
162   
 
163  57 toggle public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
164    AlignmentPanel ap, boolean showGUI, boolean dontQueryServices)
165    {
166   
167    // which FTS engine to use
168  57 data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
169  57 initDialog();
170   
171  57 this.ap = ap;
172  57 this.selectedSequence = selectedSeq;
173  57 this.selectedSequences = selectedSeqs;
174  57 this.progressIndicator = (ap == null) ? null : ap.alignFrame;
175  57 this.showChooserGUI = showGUI;
176  57 this.dontQueryServices = dontQueryServices;
177  57 init();
178   
179    }
180   
181    /**
182    * sets canQueryTDB if protein sequences without a canonical uniprot ref or at
183    * least one structure are discovered.
184    */
 
185  3 toggle private void populateSeqsWithoutSourceDBRef()
186    {
187  3 seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
188  3 boolean needCanonical = false;
189  3 for (SequenceI seq : selectedSequences)
190    {
191  3 if (seq.isProtein())
192    {
193  3 int dbRef = ThreeDBStructureChooserQuerySource
194    .checkUniprotRefs(seq.getDBRefs());
195  3 if (dbRef < 0)
196    {
197  2 if (dbRef == -1)
198    {
199    // need to retrieve canonicals
200  1 needCanonical = true;
201  1 seqsWithoutSourceDBRef.add(seq);
202    }
203    else
204    {
205    // could be a sequence with pdb ref
206  1 if (seq.getAllPDBEntries() == null
207    || seq.getAllPDBEntries().size() == 0)
208    {
209  0 seqsWithoutSourceDBRef.add(seq);
210    }
211    }
212    }
213    }
214    }
215    // retrieve database refs for protein sequences
216  3 if (!seqsWithoutSourceDBRef.isEmpty())
217    {
218  1 canQueryTDB = true;
219  1 if (needCanonical)
220    {
221    // triggers display of the 'Query TDB' button
222  1 notQueriedTDBYet = true;
223    }
224    }
225    };
226   
227    /**
228    * Initializes parameters used by the Structure Chooser Panel
229    */
 
230  57 toggle protected void init()
231    {
232  57 if (!Jalview.isHeadlessMode())
233    {
234  55 progressBar = new ProgressBar(this.statusPanel, this.statusBar);
235    }
236   
237  57 chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
238  57 btn_queryTDB.addActionListener(new ActionListener()
239    {
240   
 
241  0 toggle @Override
242    public void actionPerformed(ActionEvent e)
243    {
244  0 promptForTDBFetch(false);
245    }
246    });
247   
248  57 if (!dontQueryServices)
249    {
250  3 Executors.defaultThreadFactory().newThread(new Runnable()
251    {
 
252  3 toggle @Override
253    public void run()
254    {
255  3 populateSeqsWithoutSourceDBRef();
256  3 initialStructureDiscovery();
257    }
258   
259    }).start();
260    }
261    else
262    {
263  54 Console.debug(
264    "Structure chooser not querying services to discover metadata.");
265    }
266    }
267   
268    // called by init
 
269  3 toggle private void initialStructureDiscovery()
270    {
271    // check which FTS engine to use
272  3 data = StructureChooserQuerySource.getQuerySourceFor(selectedSequences);
273   
274    // ensure a filter option is in force for search
275  3 populateFilterComboBox(true, cachedPDBExists);
276   
277    // looks for any existing structures already loaded
278    // for the sequences (the cached ones)
279    // then queries the StructureChooserQuerySource to
280    // discover more structures.
281    //
282    // Possible optimisation is to only begin querying
283    // the structure chooser if there are no cached structures.
284   
285  3 long startTime = System.currentTimeMillis();
286  3 updateProgressIndicator(
287    MessageManager.getString("status.loading_cached_pdb_entries"),
288    startTime);
289  3 loadLocalCachedPDBEntries();
290  3 updateProgressIndicator(null, startTime);
291  3 updateProgressIndicator(
292    MessageManager.getString("status.searching_for_pdb_structures"),
293    startTime);
294  3 fetchStructuresMetaData();
295    // revise filter options if no results were found
296  3 populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
297  3 discoverStructureViews();
298  2 updateProgressIndicator(null, startTime);
299  2 mainFrame.setVisible(showChooserGUI);
300  2 updateCurrentView();
301    }
302   
303    /**
304    * raises dialog for Uniprot fetch followed by 3D beacons search
305    *
306    * @param ignoreGui
307    * - when true, don't ask, just fetch
308    */
 
309  0 toggle public void promptForTDBFetch(boolean ignoreGui)
310    {
311  0 final long progressId = System.currentTimeMillis();
312   
313    // final action after prompting and discovering db refs
314  0 final Runnable strucDiscovery = new Runnable()
315    {
 
316  0 toggle @Override
317    public void run()
318    {
319  0 mainFrame.setEnabled(false);
320  0 cmb_filterOption.setEnabled(false);
321  0 progressBar.setProgressBar(
322    MessageManager.getString("status.searching_3d_beacons"),
323    progressId);
324  0 btn_queryTDB.setEnabled(false);
325    // TODO: warn if no accessions discovered
326  0 populateSeqsWithoutSourceDBRef();
327    // redo initial discovery - this time with 3d beacons
328    // Executors.
329  0 previousWantedFields = null;
330  0 lastSelected = (FilterOption) cmb_filterOption.getSelectedItem();
331  0 cmb_filterOption.setSelectedItem(null);
332  0 cachedPDBExists=false; // reset to initial
333  0 initialStructureDiscovery();
334  0 if (!isStructuresDiscovered())
335    {
336  0 progressBar.setProgressBar(MessageManager.getString(
337    "status.no_structures_discovered_from_3d_beacons"),
338    progressId);
339  0 btn_queryTDB.setToolTipText(MessageManager.getString(
340    "status.no_structures_discovered_from_3d_beacons"));
341  0 btn_queryTDB.setEnabled(false);
342  0 pnl_queryTDB.setVisible(false);
343    }
344    else
345    {
346  0 cmb_filterOption.setSelectedIndex(0); // select 'best'
347  0 btn_queryTDB.setVisible(false);
348  0 pnl_queryTDB.setVisible(false);
349  0 progressBar.setProgressBar(null, progressId);
350    }
351  0 mainFrame.setEnabled(true);
352  0 cmb_filterOption.setEnabled(true);
353    }
354    };
355   
356  0 final FetchFinishedListenerI afterDbRefFetch = new FetchFinishedListenerI()
357    {
358   
 
359  0 toggle @Override
360    public void finished()
361    {
362    // filter has been selected, so we set flag to remove ourselves
363  0 notQueriedTDBYet = false;
364    // new thread to discover structures - via 3d beacons
365  0 Executors.defaultThreadFactory().newThread(strucDiscovery).start();
366   
367    }
368    };
369   
370    // fetch db refs if OK pressed
371  0 final Runnable discoverCanonicalDBrefs = () -> {
372  0 btn_queryTDB.setEnabled(false);
373  0 populateSeqsWithoutSourceDBRef();
374   
375  0 final int y = seqsWithoutSourceDBRef.size();
376  0 if (y > 0)
377    {
378  0 final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
379    .toArray(new SequenceI[y]);
380  0 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
381    progressBar, new DbSourceProxy[]
382    { new jalview.ws.dbsources.Uniprot() }, null, false);
383  0 dbRefFetcher.addListener(afterDbRefFetch);
384    // ideally this would also gracefully run with callbacks
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  0 StructureChooser thisSC = this;
410  0 JvOptionPane.newOptionDialog(thisSC.getFrame())
411    .setResponseHandler(JvOptionPane.OK_OPTION,
412    discoverCanonicalDBrefs)
413    .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
414    .setResponseHandler(JvOptionPane.NO_OPTION, revertview)
415    .showDialog(
416    MessageManager.formatMessage(
417    "label.fetch_references_for_3dbeacons",
418    seqsWithoutSourceDBRef.size()),
419    MessageManager
420    .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.getInstance() != 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.getInstance()
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  1 filteredResponse.addAll(resultList.getSearchSummary());
646    }
647    }
648   
649  1 String totalTime = (System.currentTimeMillis() - startTime)
650    + " milli secs";
651  1 if (!filteredResponse.isEmpty())
652    {
653  1 final int filterResponseCount = filteredResponse.size();
654  1 Collection<FTSData> reorderedStructuresSet = new LinkedHashSet<>();
655  1 reorderedStructuresSet.addAll(filteredResponse);
656  1 reorderedStructuresSet.addAll(discoveredStructuresSet);
657  1 getResultTable()
658    .setModel(data.getTableModel(reorderedStructuresSet));
659   
660  1 FTSRestResponse.configureTableColumn(getResultTable(),
661    wantedFields, tempUserPrefs);
662  1 getResultTable().getColumn("Ref Sequence").setPreferredWidth(120);
663  1 getResultTable().getColumn("Ref Sequence").setMinWidth(100);
664  1 getResultTable().getColumn("Ref Sequence").setMaxWidth(200);
665    // Update table selection model here
666  1 getResultTable().addRowSelectionInterval(0,
667    filterResponseCount - 1);
668  1 mainFrame.setTitle(MessageManager.formatMessage(
669    "label.structure_chooser_filter_time", totalTime));
670    }
671    else
672    {
673  0 mainFrame.setTitle(MessageManager.formatMessage(
674    "label.structure_chooser_filter_time", totalTime));
675  0 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  1 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  0 if (currentView == VIEWS_FILTER
1007    && data instanceof ThreeDBStructureChooserQuerySource)
1008    {
1009  0 TDB_FTSData row = ((ThreeDBStructureChooserQuerySource) data)
1010    .getFTSDataFor(getResultTable(), selectedRow,
1011    discoveredStructuresSet);
1012  0 String pageUrl = row.getModelViewUrl();
1013   
1014  0 JPopupMenu popup = new JPopupMenu("3D Beacons");
1015  0 JMenuItem viewUrl = new JMenuItem("View model web page");
1016  0 if (pageUrl == null || "".equals(pageUrl.trim()))
1017    {
1018  0 viewUrl.setEnabled(false);
1019  0 viewUrl.setText("No model page available.");
1020    }
1021  0 viewUrl.addActionListener(new ActionListener()
1022    {
 
1023  0 toggle @Override
1024    public void actionPerformed(ActionEvent e)
1025    {
1026  0 Desktop.showUrl(pageUrl);
1027    }
1028    });
1029  0 popup.add(viewUrl);
1030  0 SwingUtilities.invokeLater(new Runnable()
1031    {
 
1032  0 toggle @Override
1033    public void run()
1034    {
1035  0 popup.show(getResultTable(), x, y);
1036    }
1037    });
1038  0 return true;
1039    }
1040    // event not handled by us
1041  0 return false;
1042    }
1043    /**
1044    * Validates inputs from the Manual PDB entry panel
1045    */
 
1046  3 toggle protected void validateAssociationEnterPdb()
1047    {
1048  3 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
1049    .getCmb_assSeq().getSelectedItem();
1050  3 lbl_pdbManualFetchStatus.setIcon(errorImage);
1051  3 lbl_pdbManualFetchStatus.setToolTipText("");
1052  3 if (txt_search.getText().length() > 0)
1053    {
1054  0 lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(true,
1055    MessageManager.formatMessage("info.no_pdb_entry_found_for",
1056    txt_search.getText())));
1057    }
1058   
1059  3 if (errorWarning.length() > 0)
1060    {
1061  0 lbl_pdbManualFetchStatus.setIcon(warningImage);
1062  0 lbl_pdbManualFetchStatus.setToolTipText(
1063    JvSwingUtils.wrapTooltip(true, errorWarning.toString()));
1064    }
1065   
1066  3 if (selectedSequences.length == 1 || !assSeqOpt.getName()
1067    .equalsIgnoreCase("-Select Associated Seq-"))
1068    {
1069  3 txt_search.setEnabled(true);
1070    // JBPNote - temporary workaround the PDB SOLR endpoint issue
1071    // probably need to create a feature flag...
1072  3 if (txt_search.getText().length()>=4) {
1073  0 isValidPBDEntry=true;
1074    }
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    private FilterOption lastSelected=null;
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  0 final StructureViewer theViewer = getTargetedStructureViewer(ssm);
1243  0 boolean superimpose = chk_superpose.isSelected();
1244  0 final int preferredHeight = pnl_filter.getHeight();
1245  0 btn_add.setEnabled(false);
1246  0 btn_newView.setEnabled(false);
1247  0 btn_cancel.setEnabled(false);
1248  0 actionsPanel.setEnabled(false);
1249   
1250  0 final String progress = MessageManager
1251    .getString("label.working_ellipsis");
1252  0 setProgressBar(progress, progress.hashCode());
1253  0 Runnable viewStruc = new Runnable()
1254    {
 
1255  0 toggle @Override
1256    public void run()
1257    {
1258  0 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
1259    .getSelectedItem());
1260  0 String currentView = selectedFilterOpt.getView();
1261  0 JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
1262    : tbl_local_pdb;
1263   
1264  0 if (currentView == VIEWS_FILTER)
1265    {
1266  0 int[] selectedRows = restable.getSelectedRows();
1267  0 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
1268  0 List<SequenceI> selectedSeqsToView = new ArrayList<>();
1269  0 pdbEntriesToView = data.collectSelectedRows(restable,
1270    selectedRows, selectedSeqsToView);
1271   
1272  0 SequenceI[] selectedSeqs = selectedSeqsToView
1273    .toArray(new SequenceI[selectedSeqsToView.size()]);
1274  0 sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
1275    selectedSeqs, superimpose, theViewer, progressBar);
1276    }
1277  0 else if (currentView == VIEWS_LOCAL_PDB)
1278    {
1279  0 int[] selectedRows = tbl_local_pdb.getSelectedRows();
1280  0 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
1281  0 int count = 0;
1282  0 int pdbIdColIndex = tbl_local_pdb.getColumn("PDB Id")
1283    .getModelIndex();
1284  0 int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
1285    .getModelIndex();
1286  0 List<SequenceI> selectedSeqsToView = new ArrayList<>();
1287  0 for (int row : selectedRows)
1288    {
1289  0 PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb
1290    .getModel()).getPDBEntryAt(row).getPdbEntry();
1291   
1292  0 pdbEntriesToView[count++] = pdbEntry;
1293  0 SequenceI selectedSeq = (SequenceI) tbl_local_pdb
1294    .getValueAt(row, refSeqColIndex);
1295  0 selectedSeqsToView.add(selectedSeq);
1296    }
1297  0 SequenceI[] selectedSeqs = selectedSeqsToView
1298    .toArray(new SequenceI[selectedSeqsToView.size()]);
1299  0 sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
1300    selectedSeqs, superimpose, theViewer, progressBar);
1301    }
1302  0 else if (currentView == VIEWS_ENTER_ID)
1303    {
1304  0 SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
1305    .getCmb_assSeq().getSelectedItem()).getSequence();
1306  0 if (userSelectedSeq != null)
1307    {
1308  0 selectedSequence = userSelectedSeq;
1309    }
1310  0 String pdbIdStr = txt_search.getText();
1311  0 PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
1312  0 if (pdbEntry == null)
1313    {
1314  0 pdbEntry = new PDBEntry();
1315  0 if (pdbIdStr.split(":").length > 1)
1316    {
1317  0 pdbEntry.setId(pdbIdStr.split(":")[0]);
1318  0 pdbEntry.setChainCode(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 = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
1330    new SequenceI[]
1331    { selectedSequence }, superimpose, theViewer,
1332    progressBar);
1333    }
1334  0 else if (currentView == VIEWS_FROM_FILE)
1335    {
1336  0 StructureChooser sc = StructureChooser.this;
1337  0 String chainCodes = sc.chains.getText();
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    // TODO: JAL-4107 - do we still need to resolve via AssociatePdbFileWithSeq ?
1348    //PDBEntry fileEntry = AssociatePdbFileWithSeq.associatePdbWithSeq(selectedPdbFileName,
1349    // DataSourceType.FILE, selectedSequence, true);
1350   
1351    // sViewer = StructureViewer.launchStructureViewer(ap, new PDBEntry[] { fileEntry },
1352    // new SequenceI[] { selectedSequence }, superimpose, theViewer,
1353    // progressBar);
1354  0 String lbn_filename;
1355   
1356  0 String[] chainCodeSet= null;
1357  0 SequenceI[] selSeq=new SequenceI[] {selectedSequence };
1358  0 if (chainCodes.length()>0 && chainCodes.trim().length()>0 && !"*".equals(chainCodes.trim()))
1359    {
1360  0 chainCodeSet=chainCodes.split(",");
1361    }
1362   
1363  0 String pdbFilename = selectedPdbFileName;
1364  0 StructureChooser.openStructureForASequence(ssm, sc, ap,
1365    selectedSequence, true,pdbFilename,DataSourceType.FILE, chainCodeSet,tft,
1366    paeFilename,
1367    false, true, false,
1368    getTargetedStructureViewer(ssm).getViewerType());
1369    }
1370  0 SwingUtilities.invokeLater(new Runnable()
1371    {
 
1372  0 toggle @Override
1373    public void run()
1374    {
1375  0 setProgressBar("Complete.", progress.hashCode());
1376  0 closeAction(preferredHeight);
1377  0 mainFrame.dispose();
1378    }
1379    });
1380    }
1381    };
1382  0 Thread runner = new Thread(viewStruc);
1383  0 runner.start();
1384  0 if (waitUntilFinished)
1385    {
1386  0 while (sViewer == null ? runner.isAlive()
1387  0 : (sViewer.sview == null ? true
1388    : !sViewer.sview.hasMapping()))
1389    {
1390  0 try
1391    {
1392  0 Thread.sleep(300);
1393    } catch (InterruptedException ie)
1394    {
1395   
1396    }
1397    }
1398    }
1399    }
1400   
1401    /**
1402    * Answers a structure viewer (new or existing) configured to superimpose
1403    * added structures or not according to the user's choice
1404    *
1405    * @param ssm
1406    * @return
1407    */
 
1408  38 toggle StructureViewer getTargetedStructureViewer(StructureSelectionManager ssm)
1409    {
1410  38 Object sv = targetView.getSelectedItem();
1411   
1412  38 return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv;
1413    }
1414   
1415    /**
1416    * Adds PDB structures to a new or existing structure viewer
1417    *
1418    * @param ssm
1419    * @param pdbEntriesToView
1420    * @param alignPanel
1421    * @param sequences
1422    * @return
1423    */
 
1424  0 toggle private StructureViewer launchStructureViewer(
1425    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1426    final AlignmentPanel alignPanel, SequenceI[] sequences)
1427    {
1428  0 return launchStructureViewer(ssm, pdbEntriesToView, alignPanel,
1429    sequences, null);
1430    }
1431   
 
1432  33 toggle public StructureViewer launchStructureViewer(
1433    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1434    final AlignmentPanel alignPanel, SequenceI[] sequences,
1435    ViewerType viewerType)
1436    {
1437  33 return launchStructureViewer(ssm, pdbEntriesToView, alignPanel,
1438    sequences, viewerType, chk_superpose.isSelected());
1439    }
1440   
 
1441  38 toggle public StructureViewer launchStructureViewer(
1442    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1443    final AlignmentPanel alignPanel, SequenceI[] sequences,
1444    ViewerType viewerType, boolean thisSuperimpose)
1445    {
1446  38 long progressId = sequences.hashCode();
1447  38 setProgressBar(MessageManager
1448    .getString("status.launching_3d_structure_viewer"), progressId);
1449  38 final StructureViewer theViewer = getTargetedStructureViewer(ssm);
1450  38 theViewer.setSuperpose(thisSuperimpose);
1451   
1452    // if we're running in --headless mode make this viewer synchronous
1453  38 if (Jalview.isHeadlessMode())
1454    {
1455  2 theViewer.setAsync(false);
1456    }
1457   
1458    /*
1459    * remember user's choice of superimpose or not
1460    */
1461  38 Cache.setProperty(AUTOSUPERIMPOSE,
1462    Boolean.valueOf(thisSuperimpose).toString());
1463   
1464  38 setProgressBar(null, progressId);
1465  38 boolean localFiles=true;
1466  38 for (PDBEntry pe:pdbEntriesToView)
1467    {
1468  48 if (pe.hasProvider() || (! pe.fakedPDBId() && pe.isAuthoritative()))
1469    {
1470  0 localFiles=false;
1471  0 break;
1472    }
1473    }
1474  38 if (!localFiles && SiftsSettings.isMapWithSifts())
1475    {
1476  0 List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
1477  0 int p = 0;
1478    // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
1479    // real PDB ID. For moment, we can also safely do this if there is already
1480    // a known mapping between the PDBEntry and the sequence.
1481  0 for (SequenceI seq : sequences)
1482    {
1483  0 PDBEntry pdbe = pdbEntriesToView[p++];
1484  0 if (pdbe != null && pdbe.getFile() != null)
1485    {
1486  0 StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
1487  0 if (smm != null && smm.length > 0)
1488    {
1489  0 for (StructureMapping sm : smm)
1490    {
1491  0 if (sm.getSequence() == seq)
1492    {
1493  0 continue;
1494    }
1495    }
1496    }
1497    }
1498  0 if (seq.getPrimaryDBRefs().isEmpty() && seq.isProtein())
1499    {
1500  0 seqsWithoutSourceDBRef.add(seq);
1501  0 continue;
1502    }
1503    }
1504  0 if (!seqsWithoutSourceDBRef.isEmpty())
1505    {
1506  0 int y = seqsWithoutSourceDBRef.size();
1507  0 setProgressBar(MessageManager.formatMessage(
1508    "status.fetching_dbrefs_for_sequences_without_valid_refs",
1509    y), progressId);
1510  0 SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
1511    .toArray(new SequenceI[y]);
1512  0 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
1513  0 dbRefFetcher.fetchDBRefs(true);
1514   
1515  0 setProgressBar("Fetch complete.", progressId); // todo i18n
1516    }
1517    }
1518  38 if (pdbEntriesToView.length > 1)
1519    {
1520  5 setProgressBar(
1521    MessageManager.getString(
1522    "status.fetching_3d_structures_for_selected_entries"),
1523    progressId);
1524  5 theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel,
1525    viewerType);
1526    }
1527    else
1528    {
1529  33 setProgressBar(MessageManager.formatMessage(
1530    "status.fetching_3d_structures_for",
1531    pdbEntriesToView[0].getId()), progressId);
1532    // Can we pass a pre-computeMappinged pdbFile?
1533  33 theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel,
1534    viewerType);
1535    }
1536  38 setProgressBar(null, progressId);
1537    // remember the last viewer we used...
1538  38 lastTargetedView = theViewer;
1539  38 return theViewer;
1540    }
1541   
1542    /**
1543    * Populates the combo-box used in associating manually fetched structures to
1544    * a unique sequence when more than one sequence selection is made.
1545    */
 
1546  2 toggle @Override
1547    protected void populateCmbAssociateSeqOptions(
1548    JComboBox<AssociateSeqOptions> cmb_assSeq,
1549    JLabel lbl_associateSeq)
1550    {
1551  2 cmb_assSeq.removeAllItems();
1552  2 cmb_assSeq.addItem(
1553    new AssociateSeqOptions("-Select Associated Seq-", null));
1554  2 lbl_associateSeq.setVisible(false);
1555  2 if (selectedSequences.length > 1)
1556    {
1557  0 for (SequenceI seq : selectedSequences)
1558    {
1559  0 cmb_assSeq.addItem(new AssociateSeqOptions(seq));
1560    }
1561    }
1562    else
1563    {
1564  2 String seqName = selectedSequence.getDisplayId(false);
1565  2 seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
1566  2 lbl_associateSeq.setText(seqName);
1567  2 lbl_associateSeq.setVisible(true);
1568  2 cmb_assSeq.setVisible(false);
1569    }
1570    }
1571   
 
1572  3 toggle protected boolean isStructuresDiscovered()
1573    {
1574  3 return discoveredStructuresSet != null
1575    && !discoveredStructuresSet.isEmpty();
1576    }
1577   
1578    protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes
1579    // this.
1580    // Doing a search for "1" or "1c" is valuable?
1581    // Those work but are enormously slow.
1582   
 
1583  0 toggle @Override
1584    protected void txt_search_ActionPerformed()
1585    {
1586  0 String text = txt_search.getText().trim();
1587  0 if (text.length() >= PDB_ID_MIN)
1588  0 new Thread()
1589    {
1590   
 
1591  0 toggle @Override
1592    public void run()
1593    {
1594  0 errorWarning.setLength(0);
1595  0 isValidPBDEntry = false;
1596  0 if (text.length() > 0)
1597    {
1598    // TODO move this pdb id search into the PDB specific
1599    // FTSSearchEngine
1600    // for moment, it will work fine as is because it is self-contained
1601  0 String searchTerm = text.toLowerCase(Locale.ROOT);
1602  0 searchTerm = searchTerm.split(":")[0];
1603    // jalview.bin.Console.outPrintln(">>>>> search term : " +
1604    // searchTerm);
1605  0 List<FTSDataColumnI> wantedFields = new ArrayList<>();
1606  0 FTSRestRequest pdbRequest = new FTSRestRequest();
1607  0 pdbRequest.setAllowEmptySeq(false);
1608  0 pdbRequest.setResponseSize(1);
1609  0 pdbRequest.setFieldToSearchBy("(pdb_id:");
1610  0 pdbRequest.setWantedFields(wantedFields);
1611  0 pdbRequest.setSearchTerm(searchTerm + ")");
1612  0 pdbRequest.setAssociatedSequence(selectedSequence);
1613  0 FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance();
1614  0 wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
1615  0 FTSRestResponse resultList;
1616  0 try
1617    {
1618  0 resultList = pdbRestClient.executeRequest(pdbRequest);
1619    } catch (Exception e)
1620    {
1621  0 errorWarning.append(e.getMessage());
1622  0 return;
1623    } finally
1624    {
1625  0 validateSelections();
1626    }
1627  0 if (resultList.getSearchSummary() != null
1628    && resultList.getSearchSummary().size() > 0)
1629    {
1630  0 isValidPBDEntry = true;
1631    }
1632    }
1633  0 validateSelections();
1634    }
1635    }.start();
1636    }
1637   
 
1638  57 toggle @Override
1639    protected void tabRefresh()
1640    {
1641  57 if (selectedSequences != null)
1642    {
1643  0 lbl_loading.setVisible(true);
1644  0 Thread refreshThread = new Thread(new Runnable()
1645    {
 
1646  0 toggle @Override
1647    public void run()
1648    {
1649  0 fetchStructuresMetaData();
1650    // populateFilterComboBox(true, cachedPDBExists);
1651   
1652  0 filterResultSet(
1653    ((FilterOption) cmb_filterOption.getSelectedItem())
1654    .getValue());
1655  0 lbl_loading.setVisible(false);
1656    }
1657    });
1658  0 refreshThread.start();
1659    }
1660    }
1661   
 
1662    public class PDBEntryTableModel extends AbstractTableModel
1663    {
1664    String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type",
1665    "File" };
1666   
1667    private List<CachedPDB> pdbEntries;
1668   
 
1669  3 toggle public PDBEntryTableModel(List<CachedPDB> pdbEntries)
1670    {
1671  3 this.pdbEntries = new ArrayList<>(pdbEntries);
1672    }
1673   
 
1674  15 toggle @Override
1675    public String getColumnName(int columnIndex)
1676    {
1677  15 return columns[columnIndex];
1678    }
1679   
 
1680  21 toggle @Override
1681    public int getRowCount()
1682    {
1683  21 return pdbEntries.size();
1684    }
1685   
 
1686  18 toggle @Override
1687    public int getColumnCount()
1688    {
1689  18 return columns.length;
1690    }
1691   
 
1692  0 toggle @Override
1693    public boolean isCellEditable(int row, int column)
1694    {
1695  0 return false;
1696    }
1697   
 
1698  0 toggle @Override
1699    public Object getValueAt(int rowIndex, int columnIndex)
1700    {
1701  0 Object value = "??";
1702  0 CachedPDB entry = pdbEntries.get(rowIndex);
1703  0 switch (columnIndex)
1704    {
1705  0 case 0:
1706  0 value = entry.getSequence();
1707  0 break;
1708  0 case 1:
1709  0 value = entry.getQualifiedId();
1710  0 break;
1711  0 case 2:
1712  0 value = entry.getPdbEntry().getChainCode() == null ? "_"
1713    : entry.getPdbEntry().getChainCode();
1714  0 break;
1715  0 case 3:
1716  0 value = entry.getPdbEntry().getType();
1717  0 break;
1718  0 case 4:
1719  0 value = entry.getPdbEntry().getFile();
1720  0 break;
1721    }
1722  0 return value;
1723    }
1724   
 
1725  0 toggle @Override
1726    public Class<?> getColumnClass(int columnIndex)
1727    {
1728  0 return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
1729    }
1730   
 
1731  0 toggle public CachedPDB getPDBEntryAt(int row)
1732    {
1733  0 return pdbEntries.get(row);
1734    }
1735   
1736    }
1737   
 
1738    private class CachedPDB
1739    {
1740    private SequenceI sequence;
1741   
1742    private PDBEntry pdbEntry;
1743   
 
1744  0 toggle public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
1745    {
1746  0 this.sequence = sequence;
1747  0 this.pdbEntry = pdbEntry;
1748    }
1749   
 
1750  0 toggle public String getQualifiedId()
1751    {
1752  0 if (pdbEntry.hasProvider())
1753    {
1754  0 return pdbEntry.getProvider() + ":" + pdbEntry.getId();
1755    }
1756  0 return pdbEntry.toString();
1757    }
1758   
 
1759  0 toggle public SequenceI getSequence()
1760    {
1761  0 return sequence;
1762    }
1763   
 
1764  0 toggle public PDBEntry getPdbEntry()
1765    {
1766  0 return pdbEntry;
1767    }
1768   
1769    }
1770   
1771    private IProgressIndicator progressBar;
1772   
 
1773  152 toggle @Override
1774    public void setProgressBar(String message, long id)
1775    {
1776  152 if (!Platform.isHeadless() && progressBar != null)
1777  144 progressBar.setProgressBar(message, id);
1778    }
1779   
 
1780  0 toggle @Override
1781    public void addProgressBar(long id, String message)
1782    {
1783  0 progressBar.addProgressBar(id, message);
1784    }
1785   
 
1786  0 toggle @Override
1787    public void removeProgressBar(long id)
1788    {
1789  0 progressBar.removeProgressBar(id);
1790    }
1791   
 
1792  0 toggle @Override
1793    public void registerHandler(long id, IProgressIndicatorHandler handler)
1794    {
1795  0 if (progressBar != null)
1796  0 progressBar.registerHandler(id, handler);
1797    }
1798   
 
1799  0 toggle @Override
1800    public boolean operationInProgress()
1801    {
1802  0 return progressBar == null ? false : progressBar.operationInProgress();
1803    }
1804   
 
1805  0 toggle public JalviewStructureDisplayI getOpenedStructureViewer()
1806    {
1807  0 return sViewer == null ? null : sViewer.sview;
1808    }
1809   
 
1810  0 toggle @Override
1811    protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
1812    {
1813  0 data.setDocFieldPrefs(newPrefs);
1814   
1815    }
1816   
1817    /**
1818    *
1819    * @return true when all initialisation threads have finished and dialog is
1820    * visible
1821    */
 
1822  12 toggle public boolean isDialogVisible()
1823    {
1824  12 return mainFrame != null && data != null && cmb_filterOption != null
1825    && mainFrame.isVisible()
1826    && cmb_filterOption.getSelectedItem() != null;
1827    }
1828    /**
1829    *
1830    * @return true if the 3D-Beacons query button will/has been displayed
1831    */
 
1832  1 toggle public boolean isCanQueryTDB()
1833    {
1834  1 return canQueryTDB;
1835    }
1836   
 
1837  1 toggle public boolean isNotQueriedTDBYet()
1838    {
1839  1 return notQueriedTDBYet;
1840    }
1841   
1842    /**
1843    * Open a single structure file for a given sequence
1844    */
 
1845  0 toggle public static void openStructureFileForSequence(
1846    StructureSelectionManager ssm, StructureChooser sc,
1847    AlignmentPanel ap, SequenceI seq, boolean prompt,
1848    String sFilename, TFType tft, String paeFilename,
1849    boolean doXferSettings)
1850    {
1851  0 openStructureFileForSequence(ssm, sc, ap, seq, prompt, sFilename, tft,
1852    paeFilename, false, true, doXferSettings, null);
1853    }
1854   
1855    /**
1856    *
1857    * @param ssm
1858    * @param sc
1859    * @param ap
1860    * @param seq
1861    * @param prompt
1862    * @param sFilename
1863    * @param tft
1864    * @param paeFilename
1865    * @param forceHeadless
1866    * @param showRefAnnotations
1867    * @param doXferSettings
1868    * @param viewerType
1869    * - when not null means the viewer will be opened, providing
1870    * forceHeadless/headless is not true
1871    * @return
1872    */
 
1873  10 toggle public static StructureViewer openStructureFileForSequence(
1874    StructureSelectionManager ssm, StructureChooser sc,
1875    AlignmentPanel ap, SequenceI seq, boolean prompt,
1876    String sFilename, TFType tft, String paeFilename,
1877    boolean forceHeadless, boolean showRefAnnotations,
1878    boolean doXferSettings, ViewerType viewerType)
1879    {
1880  10 return openStructureForASequence(ssm, sc, ap, seq, prompt, sFilename,
1881    DataSourceType.FILE, tft, paeFilename, forceHeadless,
1882    showRefAnnotations, doXferSettings, viewerType);
1883    }
 
1884  47 toggle public static StructureViewer openStructureForASequence(
1885    StructureSelectionManager ssm, StructureChooser sc,
1886    AlignmentPanel ap, SequenceI seq, boolean prompt,
1887    String strucDloc, DataSourceType strucDsource, TFType tft,
1888    String paeFileSource, boolean forceHeadless,
1889    boolean showRefAnnotations, boolean doXferSettings,
1890    ViewerType viewerType)
1891    {
1892  47 return openStructureForASequence(ssm, sc, ap, seq, prompt, strucDloc, strucDsource, null,
1893    tft, paeFileSource, forceHeadless,
1894    showRefAnnotations, doXferSettings, viewerType);
1895    }
1896    /**
1897    * Import and view structure data for one or more sequences
1898    *
1899    * @param ssm
1900    * - null or a specific StructureSelectionManageer
1901    * @param sc
1902    * - null or a live StructureChooser instance to get data from
1903    * @param ap
1904    * - alignmentPanel to associate viewer with
1905    * @param seq
1906    * - sequence to associate structure with
1907    * @param prompt
1908    * - when true will raise an error dialog
1909    * @param strucDloc
1910    * - string resolving to structure data using strucDsoruce
1911    * @param strucDsource
1912    * - how to get data from strucDloc and paeFileSource or null if they
1913    * are file paths
1914    * @param chains
1915    * - null or one or more chains to associated with the sequence
1916    * @param tft
1917    * - temperature factor type for the datasource
1918    * @param paeFileSource
1919    * - a PAE matrix for the structure data
1920    * @param forceHeadless
1921    * - when true override any and all gui ops
1922    * @param showRefAnnotations
1923    * - automatically display reference annotations associated with
1924    * structure
1925    * @param doXferSettings
1926    * - TODO CLARIFY transfer specific import settings for structureData
1927    * @param viewerType
1928    * - when not null means the viewer will be opened, providing
1929    * forceHeadless/headless is not true
1930    * @return null or StructureViewer instance
1931    */
 
1932  49 toggle public static StructureViewer openStructureForASequence(
1933    StructureSelectionManager ssm, StructureChooser sc,
1934    AlignmentPanel ap, SequenceI seq, boolean prompt,
1935    String strucDloc, DataSourceType strucDsource, String[] chains, TFType tft,
1936    String paeFileSource, boolean forceHeadless,
1937    boolean showRefAnnotations, boolean doXferSettings,
1938    ViewerType viewerType)
1939    {
1940  49 StructureViewer sv = null;
1941  49 boolean headless = forceHeadless;
1942  49 if (sc == null)
1943    {
1944    // headless = true;
1945  49 prompt = false;
1946    // suppress structure viewer's external service queries
1947  49 sc = new StructureChooser(new SequenceI[] { seq }, seq, ap, false,
1948    true);
1949    }
1950  49 if (ssm == null)
1951    {
1952  49 ssm = ap.getStructureSelectionManager();
1953  49 StructureSelectionManager.doConfigureStructurePrefs(ssm);
1954    }
1955  49 String paeFilename = null;
1956  49 if (paeFileSource != null)
1957    {
1958  32 if (strucDsource == null || strucDsource.equals(DataSourceType.FILE))
1959    {
1960  29 paeFilename = paeFileSource;
1961    }
1962    else
1963    {
1964  3 if (DataSourceType.URL.equals(strucDsource))
1965    {
1966  3 try
1967    {
1968  3 File tfile = jalview.ws.dbsources.EBIAlfaFold
1969    .fetchAPAE_from(null, paeFileSource);
1970  3 paeFilename = tfile.getAbsolutePath();
1971    } catch (IOException ex)
1972    {
1973  0 Console.error(
1974    "Couldn't retrieve PAE file from URL " + paeFileSource,
1975    ex);
1976    }
1977   
1978    }
1979    else
1980    {
1981  0 Console.warn("Can't handle PAE data location type " + strucDsource
1982    + " for given structure " + strucDloc);
1983  0 ;
1984    }
1985    }
1986    }
1987  49 List<PDBEntry> fileEntries=new ArrayList<PDBEntry>();
1988  49 List<SequenceI> seqEntries=new ArrayList<SequenceI>();
1989  47 if (chains!=null) {
1990  0 for (String chain:chains)
1991    {
1992   
1993  0 PDBEntry fileEntry = AssociatePdbFileWithSeq.associatePdbWithSeq(
1994    strucDloc,
1995  0 strucDsource != null ? strucDsource : DataSourceType.FILE,
1996    seq, new String[] { chain }, prompt, Desktop.getInstance(), tft, paeFilename,
1997    doXferSettings);
1998    // why didn't associatePdbFile actually add a PDBEntry ?
1999    // why didn't the fileEntry have a chaincode ?
2000  0 fileEntry.setChainCode(chain);
2001  0 fileEntries.add(fileEntry);
2002  0 seq.addPDBId(fileEntry);
2003  0 seqEntries.add(seq);
2004   
2005    }
2006    }
2007    else
2008    {
2009   
2010  47 PDBEntry fileEntry = AssociatePdbFileWithSeq.associatePdbWithSeq(
2011    strucDloc,
2012  47 strucDsource != null ? strucDsource : DataSourceType.FILE,
2013    seq, prompt, Desktop.getInstance(), tft, paeFilename,
2014    doXferSettings);
2015  47 fileEntries.add(fileEntry);
2016  47 seqEntries.add(seq);
2017    }
2018    // if headless, "false" in the sc constructor above will avoid GUI behaviour
2019    // in sc.launchStructureViewer()
2020  49 if (!headless && !(viewerType == null))
2021    {
2022  33 sv = sc.launchStructureViewer(ssm, fileEntries.toArray(new PDBEntry[0]), ap,
2023    seqEntries.toArray(new SequenceI[0]), viewerType);
2024    // foo
2025  33 sv.getJalviewStructureDisplay().raiseViewer();
2026    }
2027   
2028  49 sc.mainFrame.dispose();
2029   
2030    // TODO should honor preferences - only show reference annotation that is
2031    // requested - JAL-4415 JAL-3124
2032  49 if (showRefAnnotations)
2033    {
2034  37 showReferenceAnnotationsForSequence(ap.alignFrame, seq);
2035    }
2036   
2037  49 return sv;
2038    }
2039   
 
2040  37 toggle public static void showReferenceAnnotationsForSequence(AlignFrame af,
2041    SequenceI sequence)
2042    {
2043  37 AlignViewport av = af.getCurrentView();
2044  37 AlignmentI al = av.getAlignment();
2045   
2046  37 List<SequenceI> forSequences = new ArrayList<>();
2047  37 forSequences.add(sequence);
2048  37 final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
2049  37 AlignmentUtils.findAddableReferenceAnnotations(forSequences, null,
2050    candidates, al);
2051  37 final SequenceGroup selectionGroup = av.getSelectionGroup();
2052  37 AlignmentUtils.addReferenceAnnotations(candidates, al, selectionGroup);
2053  37 for (AlignmentViewPanel ap : af.getAlignPanels())
2054    {
2055    // required to readjust the height and position of the PAE
2056    // annotation
2057  37 ap.adjustAnnotationHeight();
2058    }
2059   
2060    }
2061   
 
2062  0 toggle @Override
2063    public JProgressBar getProgressBar(long id)
2064    {
2065  0 return progressBar.getProgressBar(id);
2066    }
2067   
 
2068  0 toggle @Override
2069    public String getMessage(long id)
2070    {
2071  0 return progressIndicator.getMessage(id);
2072    }
2073   
 
2074  0 toggle @Override
2075    public void setProgressBarMessage(long id, String message)
2076    {
2077  0 progressIndicator.setProgressBarMessage(id, message);
2078    }
2079    }