Clover icon

Coverage Report

  1. Project Clover database Fri Jun 19 2026 11:35:32 BST
  2. Package jalview.gui

File StructureChooser.java

 

Coverage histogram

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

Code metrics

236
635
75
3
2,009
1,538
232
0.37
8.47
25
3.09

Classes

Class Line # Actions
StructureChooser 99 602 212
0.411173241.1%
StructureChooser.PDBEntryTableModel 1641 26 15
0.2105263221.1%
StructureChooser.CachedPDB 1717 7 5
0.00%
 

Contributing tests

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