Clover icon

Coverage Report

  1. Project Clover database Tue Nov 4 2025 11:21:43 GMT
  2. Package jalview.gui

File StructureChooser.java

 

Coverage histogram

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

Code metrics

234
633
75
3
2,004
1,535
231
0.36
8.44
25
3.08

Classes

Class Line # Actions
StructureChooser 99 600 211
0.410774441.1%
StructureChooser.PDBEntryTableModel 1636 26 15
0.2105263221.1%
StructureChooser.CachedPDB 1712 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  3 if (isValidPBDEntry)
1075    {
1076  0 btn_add.setEnabled(true);
1077  0 lbl_pdbManualFetchStatus.setToolTipText("");
1078  0 lbl_pdbManualFetchStatus.setIcon(goodImage);
1079    }
1080    }
1081    else
1082    {
1083  0 txt_search.setEnabled(false);
1084  0 lbl_pdbManualFetchStatus.setIcon(errorImage);
1085    }
1086    }
1087   
1088    /**
1089    * Validates inputs for the manual PDB file selection options
1090    */
 
1091  0 toggle protected void validateAssociationFromFile()
1092    {
1093  0 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
1094    .getCmb_assSeq().getSelectedItem();
1095    // lbl_fromFileStatus.setIcon(errorImage);
1096  0 String pdbFileString = "";
1097  0 String pdbFileTooltip = "";
1098  0 if (selectedSequences.length == 1 || (assSeqOpt != null && !assSeqOpt
1099    .getName().equalsIgnoreCase("-Select Associated Seq-")))
1100    {
1101  0 btn_pdbFromFile.setEnabled(true);
1102  0 if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
1103    {
1104  0 btn_add.setEnabled(true);
1105    // lbl_fromFileStatus.setIcon(goodImage);
1106  0 pdbFileString = new File(selectedPdbFileName).getName();
1107  0 pdbFileTooltip = new File(selectedPdbFileName).getAbsolutePath();
1108  0 setPdbOptionsEnabled(true);
1109    }
1110    else
1111    {
1112  0 pdbFileString = MessageManager.getString("label.none");
1113  0 pdbFileTooltip = MessageManager.getString("label.nothing_selected");
1114  0 setPdbOptionsEnabled(false);
1115    }
1116    }
1117    else
1118    {
1119  0 btn_pdbFromFile.setEnabled(false);
1120  0 setPdbOptionsEnabled(false);
1121    // lbl_fromFileStatus.setIcon(errorImage);
1122  0 pdbFileString = MessageManager.getString("label.none");
1123  0 pdbFileTooltip = MessageManager.getString("label.nothing_selected");
1124    }
1125  0 lbl_pdbFile.setText(pdbFileString);
1126  0 lbl_pdbFile.setToolTipText(pdbFileTooltip);
1127   
1128    // PAE file choice
1129  0 String paeFileString = "";
1130  0 String paeFileTooltip = "";
1131  0 if (localPdbPaeMatrixFileName != null
1132    && localPdbPaeMatrixFileName.length() > 0)
1133    {
1134  0 paeFileString = new File(localPdbPaeMatrixFileName).getName();
1135  0 paeFileTooltip = new File(localPdbPaeMatrixFileName)
1136    .getAbsolutePath();
1137    }
1138    else
1139    {
1140  0 paeFileString = MessageManager.getString("label.none");
1141  0 paeFileTooltip = MessageManager.getString("label.nothing_selected");
1142    }
1143  0 lbl_paeFile.setText(paeFileString);
1144  0 lbl_paeFile.setToolTipText(paeFileTooltip);
1145    }
1146   
 
1147  2 toggle @Override
1148    protected void cmbAssSeqStateChanged()
1149    {
1150  2 validateSelections();
1151    }
1152   
1153    private FilterOption lastSelected = null;
1154   
1155    /**
1156    * Handles the state change event for the 'filter' combo-box and 'invert'
1157    * check-box
1158    */
 
1159  0 toggle @Override
1160    protected void stateChanged(ItemEvent e)
1161    {
1162  0 if (e.getSource() instanceof JCheckBox)
1163    {
1164  0 updateCurrentView();
1165    }
1166    else
1167    {
1168  0 if (e.getStateChange() == ItemEvent.SELECTED)
1169    {
1170  0 updateCurrentView();
1171    }
1172    }
1173   
1174    }
1175   
1176    /**
1177    * select structures for viewing by their PDB IDs
1178    *
1179    * @param pdbids
1180    * @return true if structures were found and marked as selected
1181    */
 
1182  0 toggle public boolean selectStructure(String... pdbids)
1183    {
1184  0 boolean found = false;
1185   
1186  0 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
1187    .getSelectedItem());
1188  0 String currentView = selectedFilterOpt.getView();
1189  0 JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
1190  0 : (currentView == VIEWS_LOCAL_PDB) ? tbl_local_pdb : null;
1191   
1192  0 if (restable == null)
1193    {
1194    // can't select (enter PDB ID, or load file - need to also select which
1195    // sequence to associate with)
1196  0 return false;
1197    }
1198   
1199  0 int pdbIdColIndex = restable.getColumn("PDB Id").getModelIndex();
1200  0 for (int r = 0; r < restable.getRowCount(); r++)
1201    {
1202  0 for (int p = 0; p < pdbids.length; p++)
1203    {
1204  0 if (String.valueOf(restable.getValueAt(r, pdbIdColIndex))
1205    .equalsIgnoreCase(pdbids[p]))
1206    {
1207  0 restable.setRowSelectionInterval(r, r);
1208  0 found = true;
1209    }
1210    }
1211    }
1212  0 return found;
1213    }
1214   
1215    /**
1216    * Handles the 'New View' action
1217    */
 
1218  0 toggle @Override
1219    protected void newView_ActionPerformed()
1220    {
1221  0 targetView.setSelectedItem(null);
1222  0 showStructures(false);
1223    }
1224   
1225    /**
1226    * Handles the 'Add to existing viewer' action
1227    */
 
1228  0 toggle @Override
1229    protected void add_ActionPerformed()
1230    {
1231  0 showStructures(false);
1232    }
1233   
1234    /**
1235    * structure viewer opened by this dialog, or null
1236    */
1237    private StructureViewer sViewer = null;
1238   
 
1239  0 toggle public void showStructures(boolean waitUntilFinished)
1240    {
1241   
1242  0 final StructureSelectionManager ssm = ap.getStructureSelectionManager();
1243   
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 = launchStructureViewer(ssm, pdbEntriesToView, ap,
1275    selectedSeqs);
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 = launchStructureViewer(ssm, pdbEntriesToView, ap,
1300    selectedSeqs);
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(
1319    pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
1320    }
1321    else
1322    {
1323  0 pdbEntry.setId(pdbIdStr);
1324    }
1325  0 pdbEntry.setType(PDBEntry.Type.PDB);
1326  0 selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
1327    }
1328   
1329  0 PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
1330  0 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
1331    new SequenceI[]
1332    { selectedSequence });
1333    }
1334  0 else if (currentView == VIEWS_FROM_FILE)
1335    {
1336  0 StructureChooser sc = StructureChooser.this;
1337  0 TFType tft = (TFType) sc.combo_tempFacAs.getSelectedItem();
1338  0 String paeFilename = sc.localPdbPaeMatrixFileName;
1339  0 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
1340    .getCmb_assSeq().getSelectedItem();
1341  0 SequenceI userSelectedSeq = assSeqOpt.getSequence();
1342  0 if (userSelectedSeq != null)
1343    {
1344  0 selectedSequence = userSelectedSeq;
1345    }
1346  0 String pdbFilename = selectedPdbFileName;
1347    // TODO - tidy up this ugly hack so we call launchStructureViewer too
1348  0 StructureChooser.openStructureFileForSequence(ssm, sc, ap,
1349    selectedSequence, true, pdbFilename, tft, paeFilename,
1350    false, true, false,
1351    getTargetedStructureViewer(ssm).getViewerType());
1352    }
1353  0 SwingUtilities.invokeLater(new Runnable()
1354    {
 
1355  0 toggle @Override
1356    public void run()
1357    {
1358  0 setProgressBar("Complete.", progress.hashCode());
1359  0 closeAction(preferredHeight);
1360  0 mainFrame.dispose();
1361    }
1362    });
1363    }
1364    };
1365  0 Thread runner = new Thread(viewStruc);
1366  0 runner.start();
1367  0 if (waitUntilFinished)
1368    {
1369  0 while (sViewer == null ? runner.isAlive()
1370  0 : (sViewer.sview == null ? true
1371    : !sViewer.sview.hasMapping()))
1372    {
1373  0 try
1374    {
1375  0 Thread.sleep(300);
1376    } catch (InterruptedException ie)
1377    {
1378   
1379    }
1380    }
1381    }
1382    }
1383   
1384    /**
1385    * Answers a structure viewer (new or existing) configured to superimpose
1386    * added structures or not according to the user's choice
1387    *
1388    * @param ssm
1389    * @return
1390    */
 
1391  53 toggle StructureViewer getTargetedStructureViewer(StructureSelectionManager ssm)
1392    {
1393  53 Object sv = targetView.getSelectedItem();
1394   
1395  53 return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv;
1396    }
1397   
1398    /**
1399    * Adds PDB structures to a new or existing structure viewer
1400    *
1401    * @param ssm
1402    * @param pdbEntriesToView
1403    * @param alignPanel
1404    * @param sequences
1405    * @return
1406    */
 
1407  0 toggle private StructureViewer launchStructureViewer(
1408    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1409    final AlignmentPanel alignPanel, SequenceI[] sequences)
1410    {
1411  0 return launchStructureViewer(ssm, pdbEntriesToView, alignPanel,
1412    sequences, null);
1413    }
1414   
 
1415  48 toggle public StructureViewer launchStructureViewer(
1416    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1417    final AlignmentPanel alignPanel, SequenceI[] sequences,
1418    ViewerType viewerType)
1419    {
1420  48 return launchStructureViewer(ssm, pdbEntriesToView, alignPanel,
1421    sequences, viewerType, chk_superpose.isSelected());
1422    }
1423   
 
1424  53 toggle public StructureViewer launchStructureViewer(
1425    StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1426    final AlignmentPanel alignPanel, SequenceI[] sequences,
1427    ViewerType viewerType, boolean thisSuperimpose)
1428    {
1429  53 long progressId = sequences.hashCode();
1430  53 setProgressBar(MessageManager
1431    .getString("status.launching_3d_structure_viewer"), progressId);
1432  53 final StructureViewer theViewer = getTargetedStructureViewer(ssm);
1433  53 theViewer.setSuperpose(thisSuperimpose);
1434   
1435    // if we're running in --headless mode make this viewer synchronous
1436  53 if (Jalview.isHeadlessMode())
1437    {
1438  19 theViewer.setAsync(false);
1439    }
1440   
1441    /*
1442    * remember user's choice of superimpose or not
1443    */
1444  53 Cache.setProperty(AUTOSUPERIMPOSE,
1445    Boolean.valueOf(thisSuperimpose).toString());
1446   
1447  53 setProgressBar(null, progressId);
1448  53 if (SiftsSettings.isMapWithSifts())
1449    {
1450  0 List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
1451  0 int p = 0;
1452    // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
1453    // real PDB ID. For moment, we can also safely do this if there is already
1454    // a known mapping between the PDBEntry and the sequence.
1455  0 for (SequenceI seq : sequences)
1456    {
1457  0 PDBEntry pdbe = pdbEntriesToView[p++];
1458  0 if (pdbe != null && pdbe.getFile() != null)
1459    {
1460  0 StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
1461  0 if (smm != null && smm.length > 0)
1462    {
1463  0 for (StructureMapping sm : smm)
1464    {
1465  0 if (sm.getSequence() == seq)
1466    {
1467  0 continue;
1468    }
1469    }
1470    }
1471    }
1472  0 if (seq.getPrimaryDBRefs().isEmpty())
1473    {
1474  0 seqsWithoutSourceDBRef.add(seq);
1475  0 continue;
1476    }
1477    }
1478  0 if (!seqsWithoutSourceDBRef.isEmpty())
1479    {
1480  0 int y = seqsWithoutSourceDBRef.size();
1481  0 setProgressBar(MessageManager.formatMessage(
1482    "status.fetching_dbrefs_for_sequences_without_valid_refs",
1483    y), progressId);
1484  0 SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
1485    .toArray(new SequenceI[y]);
1486  0 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
1487  0 dbRefFetcher.fetchDBRefs(true);
1488   
1489  0 setProgressBar("Fetch complete.", progressId); // todo i18n
1490    }
1491    }
1492  53 if (pdbEntriesToView.length > 1)
1493    {
1494  5 setProgressBar(
1495    MessageManager.getString(
1496    "status.fetching_3d_structures_for_selected_entries"),
1497    progressId);
1498  5 theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel,
1499    viewerType);
1500    }
1501    else
1502    {
1503  48 setProgressBar(MessageManager.formatMessage(
1504    "status.fetching_3d_structures_for",
1505    pdbEntriesToView[0].getId()), progressId);
1506    // Can we pass a pre-computeMappinged pdbFile?
1507  48 theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel,
1508    viewerType);
1509    }
1510  53 setProgressBar(null, progressId);
1511    // remember the last viewer we used...
1512  53 lastTargetedView = theViewer;
1513  53 return theViewer;
1514    }
1515   
1516    /**
1517    * Populates the combo-box used in associating manually fetched structures to
1518    * a unique sequence when more than one sequence selection is made.
1519    */
 
1520  2 toggle @Override
1521    protected void populateCmbAssociateSeqOptions(
1522    JComboBox<AssociateSeqOptions> cmb_assSeq,
1523    JLabel lbl_associateSeq)
1524    {
1525  2 cmb_assSeq.removeAllItems();
1526  2 cmb_assSeq.addItem(
1527    new AssociateSeqOptions("-Select Associated Seq-", null));
1528  2 lbl_associateSeq.setVisible(false);
1529  2 if (selectedSequences.length > 1)
1530    {
1531  0 for (SequenceI seq : selectedSequences)
1532    {
1533  0 cmb_assSeq.addItem(new AssociateSeqOptions(seq));
1534    }
1535    }
1536    else
1537    {
1538  2 String seqName = selectedSequence.getDisplayId(false);
1539  2 seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
1540  2 lbl_associateSeq.setText(seqName);
1541  2 lbl_associateSeq.setVisible(true);
1542  2 cmb_assSeq.setVisible(false);
1543    }
1544    }
1545   
 
1546  3 toggle protected boolean isStructuresDiscovered()
1547    {
1548  3 return discoveredStructuresSet != null
1549    && !discoveredStructuresSet.isEmpty();
1550    }
1551   
1552    protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes
1553    // this.
1554    // Doing a search for "1" or "1c" is valuable?
1555    // Those work but are enormously slow.
1556   
 
1557  0 toggle @Override
1558    protected void txt_search_ActionPerformed()
1559    {
1560  0 String text = txt_search.getText().trim();
1561  0 if (text.length() >= PDB_ID_MIN)
1562  0 new Thread()
1563    {
1564   
 
1565  0 toggle @Override
1566    public void run()
1567    {
1568  0 errorWarning.setLength(0);
1569  0 isValidPBDEntry = false;
1570  0 if (text.length() > 0)
1571    {
1572    // TODO move this pdb id search into the PDB specific
1573    // FTSSearchEngine
1574    // for moment, it will work fine as is because it is self-contained
1575  0 String searchTerm = text.toLowerCase(Locale.ROOT);
1576  0 searchTerm = searchTerm.split(":")[0];
1577    // jalview.bin.Console.outPrintln(">>>>> search term : " +
1578    // searchTerm);
1579  0 List<FTSDataColumnI> wantedFields = new ArrayList<>();
1580  0 FTSRestRequest pdbRequest = new FTSRestRequest();
1581  0 pdbRequest.setAllowEmptySeq(false);
1582  0 pdbRequest.setResponseSize(1);
1583  0 pdbRequest.setFieldToSearchBy("(pdb_id:");
1584  0 pdbRequest.setWantedFields(wantedFields);
1585  0 pdbRequest.setSearchTerm(searchTerm + ")");
1586  0 pdbRequest.setAssociatedSequence(selectedSequence);
1587  0 FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance();
1588  0 wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
1589  0 FTSRestResponse resultList;
1590  0 try
1591    {
1592  0 resultList = pdbRestClient.executeRequest(pdbRequest);
1593    } catch (Exception e)
1594    {
1595  0 errorWarning.append(e.getMessage());
1596  0 return;
1597    } finally
1598    {
1599  0 validateSelections();
1600    }
1601  0 if (resultList.getSearchSummary() != null
1602    && resultList.getSearchSummary().size() > 0)
1603    {
1604  0 isValidPBDEntry = true;
1605    }
1606    }
1607  0 validateSelections();
1608    }
1609    }.start();
1610    }
1611   
 
1612  72 toggle @Override
1613    protected void tabRefresh()
1614    {
1615  72 if (selectedSequences != null)
1616    {
1617  0 lbl_loading.setVisible(true);
1618  0 Thread refreshThread = new Thread(new Runnable()
1619    {
 
1620  0 toggle @Override
1621    public void run()
1622    {
1623  0 fetchStructuresMetaData();
1624    // populateFilterComboBox(true, cachedPDBExists);
1625   
1626  0 filterResultSet(
1627    ((FilterOption) cmb_filterOption.getSelectedItem())
1628    .getValue());
1629  0 lbl_loading.setVisible(false);
1630    }
1631    });
1632  0 refreshThread.start();
1633    }
1634    }
1635   
 
1636    public class PDBEntryTableModel extends AbstractTableModel
1637    {
1638    String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type",
1639    "File" };
1640   
1641    private List<CachedPDB> pdbEntries;
1642   
 
1643  3 toggle public PDBEntryTableModel(List<CachedPDB> pdbEntries)
1644    {
1645  3 this.pdbEntries = new ArrayList<>(pdbEntries);
1646    }
1647   
 
1648  15 toggle @Override
1649    public String getColumnName(int columnIndex)
1650    {
1651  15 return columns[columnIndex];
1652    }
1653   
 
1654  20 toggle @Override
1655    public int getRowCount()
1656    {
1657  20 return pdbEntries.size();
1658    }
1659   
 
1660  18 toggle @Override
1661    public int getColumnCount()
1662    {
1663  18 return columns.length;
1664    }
1665   
 
1666  0 toggle @Override
1667    public boolean isCellEditable(int row, int column)
1668    {
1669  0 return false;
1670    }
1671   
 
1672  0 toggle @Override
1673    public Object getValueAt(int rowIndex, int columnIndex)
1674    {
1675  0 Object value = "??";
1676  0 CachedPDB entry = pdbEntries.get(rowIndex);
1677  0 switch (columnIndex)
1678    {
1679  0 case 0:
1680  0 value = entry.getSequence();
1681  0 break;
1682  0 case 1:
1683  0 value = entry.getQualifiedId();
1684  0 break;
1685  0 case 2:
1686  0 value = entry.getPdbEntry().getChainCode() == null ? "_"
1687    : entry.getPdbEntry().getChainCode();
1688  0 break;
1689  0 case 3:
1690  0 value = entry.getPdbEntry().getType();
1691  0 break;
1692  0 case 4:
1693  0 value = entry.getPdbEntry().getFile();
1694  0 break;
1695    }
1696  0 return value;
1697    }
1698   
 
1699  0 toggle @Override
1700    public Class<?> getColumnClass(int columnIndex)
1701    {
1702  0 return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
1703    }
1704   
 
1705  0 toggle public CachedPDB getPDBEntryAt(int row)
1706    {
1707  0 return pdbEntries.get(row);
1708    }
1709   
1710    }
1711   
 
1712    private class CachedPDB
1713    {
1714    private SequenceI sequence;
1715   
1716    private PDBEntry pdbEntry;
1717   
 
1718  0 toggle public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
1719    {
1720  0 this.sequence = sequence;
1721  0 this.pdbEntry = pdbEntry;
1722    }
1723   
 
1724  0 toggle public String getQualifiedId()
1725    {
1726  0 if (pdbEntry.hasProvider())
1727    {
1728  0 return pdbEntry.getProvider() + ":" + pdbEntry.getId();
1729    }
1730  0 return pdbEntry.toString();
1731    }
1732   
 
1733  0 toggle public SequenceI getSequence()
1734    {
1735  0 return sequence;
1736    }
1737   
 
1738  0 toggle public PDBEntry getPdbEntry()
1739    {
1740  0 return pdbEntry;
1741    }
1742   
1743    }
1744   
1745    private IProgressIndicator progressBar;
1746   
 
1747  212 toggle @Override
1748    public void setProgressBar(String message, long id)
1749    {
1750  212 if (!Platform.isHeadless() && progressBar != null)
1751  136 progressBar.setProgressBar(message, id);
1752    }
1753   
 
1754  0 toggle @Override
1755    public void registerHandler(long id, IProgressIndicatorHandler handler)
1756    {
1757  0 if (progressBar != null)
1758  0 progressBar.registerHandler(id, handler);
1759    }
1760   
 
1761  0 toggle @Override
1762    public boolean operationInProgress()
1763    {
1764  0 return progressBar == null ? false : progressBar.operationInProgress();
1765    }
1766   
 
1767  0 toggle public JalviewStructureDisplayI getOpenedStructureViewer()
1768    {
1769  0 return sViewer == null ? null : sViewer.sview;
1770    }
1771   
 
1772  0 toggle @Override
1773    protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
1774    {
1775  0 data.setDocFieldPrefs(newPrefs);
1776   
1777    }
1778   
1779    /**
1780    *
1781    * @return true when all initialisation threads have finished and dialog is
1782    * visible
1783    */
 
1784  12 toggle public boolean isDialogVisible()
1785    {
1786  12 return mainFrame != null && data != null && cmb_filterOption != null
1787    && mainFrame.isVisible()
1788    && cmb_filterOption.getSelectedItem() != null;
1789    }
1790   
1791    /**
1792    *
1793    * @return true if the 3D-Beacons query button will/has been displayed
1794    */
 
1795  1 toggle public boolean isCanQueryTDB()
1796    {
1797  1 return canQueryTDB;
1798    }
1799   
 
1800  1 toggle public boolean isNotQueriedTDBYet()
1801    {
1802  1 return notQueriedTDBYet;
1803    }
1804   
1805    /**
1806    * Open a single structure file for a given sequence
1807    */
 
1808  0 toggle public static void openStructureFileForSequence(
1809    StructureSelectionManager ssm, StructureChooser sc,
1810    AlignmentPanel ap, SequenceI seq, boolean prompt,
1811    String sFilename, TFType tft, String paeFilename,
1812    boolean doXferSettings)
1813    {
1814  0 openStructureFileForSequence(ssm, sc, ap, seq, prompt, sFilename, tft,
1815    paeFilename, false, true, doXferSettings, null);
1816    }
1817   
1818    /**
1819    *
1820    * @param ssm
1821    * @param sc
1822    * @param ap
1823    * @param seq
1824    * @param prompt
1825    * @param sFilename
1826    * @param tft
1827    * @param paeFilename
1828    * @param forceHeadless
1829    * @param showRefAnnotations
1830    * @param doXferSettings
1831    * @param viewerType
1832    * - when not null means the viewer will be opened, providing
1833    * forceHeadless/headless is not true
1834    * @return
1835    */
 
1836  10 toggle public static StructureViewer openStructureFileForSequence(
1837    StructureSelectionManager ssm, StructureChooser sc,
1838    AlignmentPanel ap, SequenceI seq, boolean prompt,
1839    String sFilename, TFType tft, String paeFilename,
1840    boolean forceHeadless, boolean showRefAnnotations,
1841    boolean doXferSettings, ViewerType viewerType)
1842    {
1843  10 return openStructureForASequence(ssm, sc, ap, seq, prompt, sFilename,
1844    DataSourceType.FILE, tft, paeFilename, forceHeadless,
1845    showRefAnnotations, doXferSettings, viewerType);
1846    }
1847   
1848    /**
1849    * Import and view structure data for one or more sequences
1850    *
1851    * @param ssm
1852    * - null or a specific StructureSelectionManageer
1853    * @param sc
1854    * - null or a live StructureChooser instance to get data from
1855    * @param ap
1856    * - alignmentPanel to associate viewer with
1857    * @param seq
1858    * - sequence to associate structure with
1859    * @param prompt
1860    * - when true will raise an error dialog
1861    * @param strucDloc
1862    * - string resolving to structure data using strucDsoruce
1863    * @param strucDsource
1864    * - how to get data from strucDloc and paeFileSource or null if they
1865    * are file paths
1866    * @param tft
1867    * - temperature factor type for the datasource
1868    * @param paeFileSource
1869    * - a PAE matrix for the structure data
1870    * @param forceHeadless
1871    * - when true override any and all gui ops
1872    * @param showRefAnnotations
1873    * - automatically display reference annotations associated with
1874    * structure
1875    * @param doXferSettings
1876    * - TODO CLARIFY transfer specific import settings for structureData
1877    * @param viewerType
1878    * - when not null means the viewer will be opened, providing
1879    * forceHeadless/headless is not true
1880    * @return null or StructureViewer instance
1881    */
 
1882  64 toggle public static StructureViewer openStructureForASequence(
1883    StructureSelectionManager ssm, StructureChooser sc,
1884    AlignmentPanel ap, SequenceI seq, boolean prompt,
1885    String strucDloc, DataSourceType strucDsource, TFType tft,
1886    String paeFileSource, boolean forceHeadless,
1887    boolean showRefAnnotations, boolean doXferSettings,
1888    ViewerType viewerType)
1889    {
1890  64 StructureViewer sv = null;
1891  64 boolean headless = forceHeadless;
1892  64 if (sc == null)
1893    {
1894    // headless = true;
1895  64 prompt = false;
1896    // suppress structure viewer's external service queries
1897  64 sc = new StructureChooser(new SequenceI[] { seq }, seq, ap, false,
1898    true);
1899    }
1900  64 if (ssm == null)
1901    {
1902  64 ssm = ap.getStructureSelectionManager();
1903  64 StructureSelectionManager.doConfigureStructurePrefs(ssm);
1904    }
1905  64 String paeFilename = null;
1906  64 if (paeFileSource != null)
1907    {
1908  39 if (strucDsource == null || strucDsource.equals(DataSourceType.FILE))
1909    {
1910  36 paeFilename = paeFileSource;
1911    }
1912    else
1913    {
1914  3 if (DataSourceType.URL.equals(strucDsource))
1915    {
1916  3 try
1917    {
1918  3 File tfile = jalview.ws.dbsources.EBIAlfaFold
1919    .fetchAPAE_from(null, paeFileSource);
1920  3 paeFilename = tfile.getAbsolutePath();
1921    } catch (IOException ex)
1922    {
1923  0 Console.error(
1924    "Couldn't retrieve PAE file from URL " + paeFileSource,
1925    ex);
1926    }
1927   
1928    }
1929    else
1930    {
1931  0 Console.warn("Can't handle PAE data location type " + strucDsource
1932    + " for given structure " + strucDloc);
1933  0 ;
1934    }
1935    }
1936    }
1937  64 PDBEntry fileEntry = new AssociatePdbFileWithSeq().associatePdbWithSeq(
1938    strucDloc,
1939  64 strucDsource != null ? strucDsource : DataSourceType.FILE, seq,
1940    prompt, Desktop.instance, tft, paeFilename, doXferSettings);
1941   
1942    // if headless, "false" in the sc constructor above will avoid GUI behaviour
1943    // in sc.launchStructureViewer()
1944  64 if (!headless && !(viewerType == null))
1945    {
1946  48 sv = sc.launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
1947    new SequenceI[]
1948    { seq }, viewerType);
1949    // foo
1950  48 sv.getJalviewStructureDisplay().raiseViewer();
1951    }
1952   
1953  64 sc.mainFrame.dispose();
1954   
1955    // TODO should honor preferences - only show reference annotation that is
1956    // requested - JAL-4415 JAL-3124
1957  64 if (showRefAnnotations)
1958    {
1959  49 showReferenceAnnotationsForSequence(ap.alignFrame, seq);
1960    }
1961   
1962  64 return sv;
1963    }
1964   
 
1965  49 toggle public static void showReferenceAnnotationsForSequence(AlignFrame af,
1966    SequenceI sequence)
1967    {
1968  49 AlignViewport av = af.getCurrentView();
1969  49 AlignmentI al = av.getAlignment();
1970   
1971  49 List<SequenceI> forSequences = new ArrayList<>();
1972  49 forSequences.add(sequence);
1973  49 final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<>();
1974  49 AlignmentUtils.findAddableReferenceAnnotations(forSequences, null,
1975    candidates, al);
1976  49 final SequenceGroup selectionGroup = av.getSelectionGroup();
1977  49 AlignmentUtils.addReferenceAnnotations(candidates, al, selectionGroup);
1978  49 for (AlignmentViewPanel ap : af.getAlignPanels())
1979    {
1980    // required to readjust the height and position of the PAE
1981    // annotation
1982  49 ap.adjustAnnotationHeight();
1983    }
1984   
1985    }
1986   
 
1987  0 toggle @Override
1988    public JProgressBar getProgressBar(long id)
1989    {
1990  0 return progressBar.getProgressBar(id);
1991    }
1992   
 
1993  0 toggle @Override
1994    public String getMessage(long id)
1995    {
1996  0 return progressIndicator.getMessage(id);
1997    }
1998   
 
1999  0 toggle @Override
2000    public void setProgressBarMessage(long id, String message)
2001    {
2002  0 progressIndicator.setProgressBarMessage(id, message);
2003    }
2004    }