2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
24 import java.awt.event.ItemEvent;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashSet;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.concurrent.Executors;
32 import javax.swing.JCheckBox;
33 import javax.swing.JComboBox;
34 import javax.swing.JLabel;
35 import javax.swing.JTable;
36 import javax.swing.SwingUtilities;
37 import javax.swing.table.AbstractTableModel;
39 import jalview.api.structures.JalviewStructureDisplayI;
40 import jalview.bin.Cache;
41 import jalview.bin.Jalview;
42 import jalview.datamodel.PDBEntry;
43 import jalview.datamodel.SequenceI;
44 import jalview.fts.api.FTSData;
45 import jalview.fts.api.FTSDataColumnI;
46 import jalview.fts.api.FTSRestClientI;
47 import jalview.fts.core.FTSDataColumnPreferences;
48 import jalview.fts.core.FTSRestRequest;
49 import jalview.fts.core.FTSRestResponse;
50 import jalview.fts.service.pdb.PDBFTSRestClient;
51 import jalview.gui.structurechooser.PDBStructureChooserQuerySource;
52 import jalview.gui.structurechooser.StructureChooserQuerySource;
53 import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource;
54 import jalview.io.DataSourceType;
55 import jalview.jbgui.FilterOption;
56 import jalview.jbgui.GStructureChooser;
57 import jalview.structure.StructureMapping;
58 import jalview.structure.StructureSelectionManager;
59 import jalview.util.MessageManager;
60 import jalview.ws.DBRefFetcher;
61 import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
62 import jalview.ws.seqfetcher.DbSourceProxy;
63 import jalview.ws.sifts.SiftsSettings;
66 * Provides the behaviors for the Structure chooser Panel
71 @SuppressWarnings("serial")
72 public class StructureChooser extends GStructureChooser
73 implements IProgressIndicator
75 private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
78 * transient combo box choice for initiating 3db fetch
80 private static final String VIEWS_QUERYING_TDB = "QUERY_3DB";
82 private SequenceI selectedSequence;
84 private SequenceI[] selectedSequences;
86 private IProgressIndicator progressIndicator;
88 private Collection<FTSData> discoveredStructuresSet;
90 private StructureChooserQuerySource data;
93 protected FTSDataColumnPreferences getFTSDocFieldPrefs()
95 return data.getDocFieldPrefs();
98 private String selectedPdbFileName;
100 private boolean isValidPBDEntry;
102 private boolean cachedPDBExists;
104 private Collection<FTSData> lastDiscoveredStructuresSet;
106 private boolean canQueryTDB = false;
108 private boolean notQueriedTDBYet = true;
110 List<SequenceI> seqsWithoutSourceDBRef = null;
112 private static StructureViewer lastTargetedView = null;
114 public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
117 // which FTS engine to use
118 data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
122 this.selectedSequence = selectedSeq;
123 this.selectedSequences = selectedSeqs;
124 this.progressIndicator = (ap == null) ? null : ap.alignFrame;
130 * sets canQueryTDB if protein sequences without a canonical uniprot ref or at
131 * least one structure are discovered.
133 private void populateSeqsWithoutSourceDBRef()
135 seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
136 boolean needCanonical = false;
137 for (SequenceI seq : selectedSequences)
141 int dbRef = ThreeDBStructureChooserQuerySource
142 .checkUniprotRefs(seq.getDBRefs());
147 // need to retrieve canonicals
148 needCanonical = true;
149 seqsWithoutSourceDBRef.add(seq);
153 // could be a sequence with pdb ref
154 if (seq.getAllPDBEntries() == null
155 || seq.getAllPDBEntries().size() == 0)
157 seqsWithoutSourceDBRef.add(seq);
163 // retrieve database refs for protein sequences
164 if (!seqsWithoutSourceDBRef.isEmpty())
169 notQueriedTDBYet = false;
175 * Initializes parameters used by the Structure Chooser Panel
177 protected void init()
179 if (!Jalview.isHeadlessMode())
181 progressBar = new ProgressBar(this.statusPanel, this.statusBar);
184 chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
186 Executors.defaultThreadFactory().newThread(new Runnable()
190 populateSeqsWithoutSourceDBRef();
191 initialStructureDiscovery();
199 private void initialStructureDiscovery()
201 // check which FTS engine to use
202 data = StructureChooserQuerySource.getQuerySourceFor(selectedSequences);
204 // ensure a filter option is in force for search
205 populateFilterComboBox(true, cachedPDBExists);
207 // looks for any existing structures already loaded
208 // for the sequences (the cached ones)
209 // then queries the StructureChooserQuerySource to
210 // discover more structures.
212 // Possible optimisation is to only begin querying
213 // the structure chooser if there are no cached structures.
215 long startTime = System.currentTimeMillis();
216 updateProgressIndicator(
217 MessageManager.getString("status.loading_cached_pdb_entries"),
219 loadLocalCachedPDBEntries();
220 updateProgressIndicator(null, startTime);
221 updateProgressIndicator(
222 MessageManager.getString("status.searching_for_pdb_structures"),
224 fetchStructuresMetaData();
225 // revise filter options if no results were found
226 populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
227 discoverStructureViews();
228 updateProgressIndicator(null, startTime);
229 mainFrame.setVisible(true);
233 private void promptForTDBFetch()
235 final long progressId = System.currentTimeMillis();
237 // final action after prompting and discovering db refs
238 final Runnable strucDiscovery = new Runnable()
243 // TODO: warn if no accessions discovered
244 populateSeqsWithoutSourceDBRef();
245 // redo initial discovery - this time with 3d beacons
247 previousWantedFields=null;
249 initialStructureDiscovery();
253 final FetchFinishedListenerI afterDbRefFetch = new FetchFinishedListenerI()
257 public void finished()
259 // filter has been selected, so we set flag to remove ourselves
260 notQueriedTDBYet = false;
261 // new thread to discover structures - via 3d beacons
262 Executors.defaultThreadFactory().newThread(strucDiscovery).start();
267 // fetch db refs if OK pressed
268 final Runnable discoverCanonicalDBrefs = new Runnable()
273 populateSeqsWithoutSourceDBRef();
275 final int y = seqsWithoutSourceDBRef.size();
278 final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
279 .toArray(new SequenceI[y]);
280 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
281 progressBar, new DbSourceProxy[]
282 { new jalview.ws.dbsources.Uniprot() }, null, false);
283 dbRefFetcher.addListener(afterDbRefFetch);
284 // ideally this would also gracefully run with callbacks
285 dbRefFetcher.fetchDBRefs(true);
287 // call finished action directly
288 afterDbRefFetch.finished();
293 final Runnable revertview = new Runnable() {
295 if (lastSelected!=null) {
296 cmb_filterOption.setSelectedItem(lastSelected);
300 // need cancel and no to result in the discoverPDB action - mocked is
302 JvOptionPane.newOptionDialog(this)
303 .setResponseHandler(JvOptionPane.OK_OPTION,
304 discoverCanonicalDBrefs)
305 .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
306 .setResponseHandler(JvOptionPane.NO_OPTION, revertview)
308 MessageManager.formatMessage(
309 "label.fetch_references_for_3dbeacons",
310 seqsWithoutSourceDBRef.size()),
312 .getString("label.3dbeacons"),
313 JvOptionPane.YES_NO_OPTION, JvOptionPane.PLAIN_MESSAGE,
315 { MessageManager.getString("action.ok"),
316 MessageManager.getString("action.cancel") },
317 MessageManager.getString("action.ok"));
321 * Builds a drop-down choice list of existing structure viewers to which new
322 * structures may be added. If this list is empty then it, and the 'Add'
323 * button, are hidden.
325 private void discoverStructureViews()
327 if (Desktop.instance != null)
329 targetView.removeAllItems();
330 if (lastTargetedView != null && !lastTargetedView.isVisible())
332 lastTargetedView = null;
334 int linkedViewsAt = 0;
335 for (StructureViewerBase view : Desktop.instance
336 .getStructureViewers(null, null))
338 StructureViewer viewHandler = (lastTargetedView != null
339 && lastTargetedView.sview == view) ? lastTargetedView
340 : StructureViewer.reconfigure(view);
342 if (view.isLinkedWith(ap))
344 targetView.insertItemAt(viewHandler, linkedViewsAt++);
348 targetView.addItem(viewHandler);
353 * show option to Add to viewer if at least 1 viewer found
355 targetView.setVisible(false);
356 if (targetView.getItemCount() > 0)
358 targetView.setVisible(true);
359 if (lastTargetedView != null)
361 targetView.setSelectedItem(lastTargetedView);
365 targetView.setSelectedIndex(0);
368 btn_add.setVisible(targetView.isVisible());
373 * Updates the progress indicator with the specified message
376 * displayed message for the operation
378 * unique handle for this indicator
380 protected void updateProgressIndicator(String message, long id)
382 if (progressIndicator != null)
384 progressIndicator.setProgressBar(message, id);
389 * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
392 void fetchStructuresMetaData()
394 long startTime = System.currentTimeMillis();
395 Collection<FTSDataColumnI> wantedFields = data.getDocFieldPrefs()
396 .getStructureSummaryFields();
398 discoveredStructuresSet = new LinkedHashSet<>();
399 HashSet<String> errors = new HashSet<>();
401 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
404 for (SequenceI seq : selectedSequences)
407 FTSRestResponse resultList;
410 resultList = data.fetchStructuresMetaData(seq, wantedFields,
411 selectedFilterOpt, !chk_invertFilter.isSelected());
412 // null response means the FTSengine didn't yield a query for this
413 // consider designing a special exception if we really wanted to be
415 if (resultList == null)
419 } catch (Exception e)
422 errors.add(e.getMessage());
425 if (resultList.getSearchSummary() != null
426 && !resultList.getSearchSummary().isEmpty())
428 discoveredStructuresSet.addAll(resultList.getSearchSummary());
432 int noOfStructuresFound = 0;
433 String totalTime = (System.currentTimeMillis() - startTime)
435 if (discoveredStructuresSet != null
436 && !discoveredStructuresSet.isEmpty())
439 .setModel(data.getTableModel(discoveredStructuresSet));
441 noOfStructuresFound = discoveredStructuresSet.size();
442 lastDiscoveredStructuresSet = discoveredStructuresSet;
443 mainFrame.setTitle(MessageManager.formatMessage(
444 "label.structure_chooser_no_of_structures",
445 noOfStructuresFound, totalTime));
449 mainFrame.setTitle(MessageManager
450 .getString("label.structure_chooser_manual_association"));
451 if (errors.size() > 0)
453 StringBuilder errorMsg = new StringBuilder();
454 for (String error : errors)
456 errorMsg.append(error).append("\n");
458 JvOptionPane.showMessageDialog(this, errorMsg.toString(),
459 MessageManager.getString("label.pdb_web-service_error"),
460 JvOptionPane.ERROR_MESSAGE);
465 protected void loadLocalCachedPDBEntries()
467 ArrayList<CachedPDB> entries = new ArrayList<>();
468 for (SequenceI seq : selectedSequences)
470 if (seq.getDatasetSequence() != null
471 && seq.getDatasetSequence().getAllPDBEntries() != null)
473 for (PDBEntry pdbEntry : seq.getDatasetSequence()
476 if (pdbEntry.getFile() != null)
478 entries.add(new CachedPDB(seq, pdbEntry));
483 cachedPDBExists = !entries.isEmpty();
484 PDBEntryTableModel tableModelx = new PDBEntryTableModel(entries);
485 tbl_local_pdb.setModel(tableModelx);
489 * Filters a given list of discovered structures based on supplied argument
491 * @param fieldToFilterBy
492 * the field to filter by
494 void filterResultSet(final String fieldToFilterBy)
496 Thread filterThread = new Thread(new Runnable()
502 long startTime = System.currentTimeMillis();
503 lbl_loading.setVisible(true);
504 Collection<FTSDataColumnI> wantedFields = data.getDocFieldPrefs()
505 .getStructureSummaryFields();
506 Collection<FTSData> filteredResponse = new HashSet<>();
507 HashSet<String> errors = new HashSet<>();
509 for (SequenceI seq : selectedSequences)
512 FTSRestResponse resultList;
515 resultList = data.selectFirstRankedQuery(seq,
516 discoveredStructuresSet, wantedFields, fieldToFilterBy,
517 !chk_invertFilter.isSelected());
519 } catch (Exception e)
522 errors.add(e.getMessage());
525 if (resultList.getSearchSummary() != null
526 && !resultList.getSearchSummary().isEmpty())
528 filteredResponse.addAll(resultList.getSearchSummary());
532 String totalTime = (System.currentTimeMillis() - startTime)
534 if (!filteredResponse.isEmpty())
536 final int filterResponseCount = filteredResponse.size();
537 Collection<FTSData> reorderedStructuresSet = new LinkedHashSet<>();
538 reorderedStructuresSet.addAll(filteredResponse);
539 reorderedStructuresSet.addAll(discoveredStructuresSet);
541 .setModel(data.getTableModel(reorderedStructuresSet));
543 FTSRestResponse.configureTableColumn(getResultTable(),
544 wantedFields, tempUserPrefs);
545 getResultTable().getColumn("Ref Sequence").setPreferredWidth(120);
546 getResultTable().getColumn("Ref Sequence").setMinWidth(100);
547 getResultTable().getColumn("Ref Sequence").setMaxWidth(200);
548 // Update table selection model here
549 getResultTable().addRowSelectionInterval(0,
550 filterResponseCount - 1);
551 mainFrame.setTitle(MessageManager.formatMessage(
552 "label.structure_chooser_filter_time", totalTime));
556 mainFrame.setTitle(MessageManager.formatMessage(
557 "label.structure_chooser_filter_time", totalTime));
558 if (errors.size() > 0)
560 StringBuilder errorMsg = new StringBuilder();
561 for (String error : errors)
563 errorMsg.append(error).append("\n");
565 JvOptionPane.showMessageDialog(null, errorMsg.toString(),
566 MessageManager.getString("label.pdb_web-service_error"),
567 JvOptionPane.ERROR_MESSAGE);
571 lbl_loading.setVisible(false);
573 validateSelections();
576 filterThread.start();
580 * Handles action event for btn_pdbFromFile
583 protected void pdbFromFile_actionPerformed()
585 // TODO: JAL-3048 not needed for Jalview-JS until JSmol dep and
588 jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
589 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
590 chooser.setFileView(new jalview.io.JalviewFileView());
591 chooser.setDialogTitle(
592 MessageManager.formatMessage("label.select_pdb_file_for",
593 selectedSequence.getDisplayId(false)));
594 chooser.setToolTipText(MessageManager.formatMessage(
595 "label.load_pdb_file_associate_with_sequence",
596 selectedSequence.getDisplayId(false)));
598 int value = chooser.showOpenDialog(null);
599 if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
601 selectedPdbFileName = chooser.getSelectedFile().getPath();
602 jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
603 validateSelections();
608 * Populates the filter combo-box options dynamically depending on discovered
611 protected void populateFilterComboBox(boolean haveData,
612 boolean cachedPDBExist)
614 populateFilterComboBox(haveData, cachedPDBExist, null);
618 * Populates the filter combo-box options dynamically depending on discovered
621 protected void populateFilterComboBox(boolean haveData,
622 boolean cachedPDBExist, FilterOption lastSel)
626 * temporarily suspend the change listener behaviour
628 cmb_filterOption.removeItemListener(this);
630 cmb_filterOption.removeAllItems();
633 List<FilterOption> filters = data
634 .getAvailableFilterOptions(VIEWS_FILTER);
635 data.updateAvailableFilterOptions(VIEWS_FILTER, filters,
636 lastDiscoveredStructuresSet);
638 for (FilterOption filter : filters)
640 if (lastSel != null && filter.equals(lastSel))
645 cmb_filterOption.addItem(filter);
649 cmb_filterOption.addItem(
650 new FilterOption(MessageManager.getString("label.enter_pdb_id"),
651 "-", VIEWS_ENTER_ID, false, null));
652 cmb_filterOption.addItem(
653 new FilterOption(MessageManager.getString("label.from_file"),
654 "-", VIEWS_FROM_FILE, false, null));
655 if (canQueryTDB && notQueriedTDBYet)
657 FilterOption queryTDBOption = new FilterOption(
658 MessageManager.getString("label.search_3dbeacons"), "-",
659 VIEWS_QUERYING_TDB, false, null);
660 cmb_filterOption.addItem(queryTDBOption);
665 FilterOption cachedOption = new FilterOption(
666 MessageManager.getString("label.cached_structures"), "-",
667 VIEWS_LOCAL_PDB, false, null);
668 cmb_filterOption.addItem(cachedOption);
671 cmb_filterOption.setSelectedItem(cachedOption);
676 cmb_filterOption.setSelectedIndex(selSet);
678 cmb_filterOption.addItemListener(this);
682 * Updates the displayed view based on the selected filter option
684 protected void updateCurrentView()
686 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
689 // first check if we need to rebuild dialog
690 if (selectedFilterOpt.getView() == VIEWS_QUERYING_TDB)
695 if (lastSelected == selectedFilterOpt)
697 // don't need to do anything, probably
700 // otherwise, record selection
701 // and update the layout and dialog accordingly
702 lastSelected = selectedFilterOpt;
704 layout_switchableViews.show(pnl_switchableViews,
705 selectedFilterOpt.getView());
706 String filterTitle = mainFrame.getTitle();
707 mainFrame.setTitle(frameTitle);
708 chk_invertFilter.setVisible(false);
710 if (selectedFilterOpt.getView() == VIEWS_FILTER)
712 mainFrame.setTitle(filterTitle);
713 // TDB Query has no invert as yet
714 chk_invertFilter.setVisible(selectedFilterOpt
715 .getQuerySource() instanceof PDBStructureChooserQuerySource);
717 if (data != selectedFilterOpt.getQuerySource()
718 || data.needsRefetch(selectedFilterOpt))
720 data = selectedFilterOpt.getQuerySource();
721 // rebuild the views completely, since prefs will also change
727 filterResultSet(selectedFilterOpt.getValue());
730 else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
731 || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
733 mainFrame.setTitle(MessageManager
734 .getString("label.structure_chooser_manual_association"));
735 idInputAssSeqPanel.loadCmbAssSeq();
736 fileChooserAssSeqPanel.loadCmbAssSeq();
738 validateSelections();
742 * Validates user selection and enables the 'Add' and 'New View' buttons if
743 * all parameters are correct (the Add button will only be visible if there is
744 * at least one existing structure viewer open). This basically means at least
745 * one structure selected and no error messages.
747 * The 'Superpose Structures' option is enabled if either more than one
748 * structure is selected, or the 'Add' to existing view option is enabled, and
749 * disabled if the only option is to open a new view of a single structure.
752 protected void validateSelections()
754 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
756 btn_add.setEnabled(false);
757 String currentView = selectedFilterOpt.getView();
758 int selectedCount = 0;
759 if (currentView == VIEWS_FILTER)
761 selectedCount = getResultTable().getSelectedRows().length;
762 if (selectedCount > 0)
764 btn_add.setEnabled(true);
767 else if (currentView == VIEWS_LOCAL_PDB)
769 selectedCount = tbl_local_pdb.getSelectedRows().length;
770 if (selectedCount > 0)
772 btn_add.setEnabled(true);
775 else if (currentView == VIEWS_ENTER_ID)
777 validateAssociationEnterPdb();
779 else if (currentView == VIEWS_FROM_FILE)
781 validateAssociationFromFile();
784 btn_newView.setEnabled(btn_add.isEnabled());
787 * enable 'Superpose' option if more than one structure is selected,
788 * or there are view(s) available to add structure(s) to
791 .setEnabled(selectedCount > 1 || targetView.getItemCount() > 0);
795 * Validates inputs from the Manual PDB entry panel
797 protected void validateAssociationEnterPdb()
799 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
800 .getCmb_assSeq().getSelectedItem();
801 lbl_pdbManualFetchStatus.setIcon(errorImage);
802 lbl_pdbManualFetchStatus.setToolTipText("");
803 if (txt_search.getText().length() > 0)
805 lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(true,
806 MessageManager.formatMessage("info.no_pdb_entry_found_for",
807 txt_search.getText())));
810 if (errorWarning.length() > 0)
812 lbl_pdbManualFetchStatus.setIcon(warningImage);
813 lbl_pdbManualFetchStatus.setToolTipText(
814 JvSwingUtils.wrapTooltip(true, errorWarning.toString()));
817 if (selectedSequences.length == 1 || !assSeqOpt.getName()
818 .equalsIgnoreCase("-Select Associated Seq-"))
820 txt_search.setEnabled(true);
823 btn_add.setEnabled(true);
824 lbl_pdbManualFetchStatus.setToolTipText("");
825 lbl_pdbManualFetchStatus.setIcon(goodImage);
830 txt_search.setEnabled(false);
831 lbl_pdbManualFetchStatus.setIcon(errorImage);
836 * Validates inputs for the manual PDB file selection options
838 protected void validateAssociationFromFile()
840 AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
841 .getCmb_assSeq().getSelectedItem();
842 lbl_fromFileStatus.setIcon(errorImage);
843 if (selectedSequences.length == 1 || (assSeqOpt != null && !assSeqOpt
844 .getName().equalsIgnoreCase("-Select Associated Seq-")))
846 btn_pdbFromFile.setEnabled(true);
847 if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
849 btn_add.setEnabled(true);
850 lbl_fromFileStatus.setIcon(goodImage);
855 btn_pdbFromFile.setEnabled(false);
856 lbl_fromFileStatus.setIcon(errorImage);
861 protected void cmbAssSeqStateChanged()
863 validateSelections();
865 private FilterOption lastSelected=null;
867 * Handles the state change event for the 'filter' combo-box and 'invert'
871 protected void stateChanged(ItemEvent e)
873 if (e.getSource() instanceof JCheckBox)
879 if (e.getStateChange() == ItemEvent.SELECTED)
888 * select structures for viewing by their PDB IDs
891 * @return true if structures were found and marked as selected
893 public boolean selectStructure(String... pdbids)
895 boolean found = false;
897 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
899 String currentView = selectedFilterOpt.getView();
900 JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
901 : (currentView == VIEWS_LOCAL_PDB) ? tbl_local_pdb : null;
903 if (restable == null)
905 // can't select (enter PDB ID, or load file - need to also select which
906 // sequence to associate with)
910 int pdbIdColIndex = restable.getColumn("PDB Id").getModelIndex();
911 for (int r = 0; r < restable.getRowCount(); r++)
913 for (int p = 0; p < pdbids.length; p++)
915 if (String.valueOf(restable.getValueAt(r, pdbIdColIndex))
916 .equalsIgnoreCase(pdbids[p]))
918 restable.setRowSelectionInterval(r, r);
927 * Handles the 'New View' action
930 protected void newView_ActionPerformed()
932 targetView.setSelectedItem(null);
933 showStructures(false);
937 * Handles the 'Add to existing viewer' action
940 protected void add_ActionPerformed()
942 showStructures(false);
946 * structure viewer opened by this dialog, or null
948 private StructureViewer sViewer = null;
950 public void showStructures(boolean waitUntilFinished)
953 final StructureSelectionManager ssm = ap.getStructureSelectionManager();
955 final int preferredHeight = pnl_filter.getHeight();
957 Runnable viewStruc = new Runnable()
962 FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
964 String currentView = selectedFilterOpt.getView();
965 JTable restable = (currentView == VIEWS_FILTER) ? getResultTable()
968 if (currentView == VIEWS_FILTER)
970 int[] selectedRows = restable.getSelectedRows();
971 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
972 List<SequenceI> selectedSeqsToView = new ArrayList<>();
973 pdbEntriesToView = data.collectSelectedRows(restable,
974 selectedRows, selectedSeqsToView);
976 SequenceI[] selectedSeqs = selectedSeqsToView
977 .toArray(new SequenceI[selectedSeqsToView.size()]);
978 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
981 else if (currentView == VIEWS_LOCAL_PDB)
983 int[] selectedRows = tbl_local_pdb.getSelectedRows();
984 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
986 int pdbIdColIndex = tbl_local_pdb.getColumn("PDB Id")
988 int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
990 List<SequenceI> selectedSeqsToView = new ArrayList<>();
991 for (int row : selectedRows)
993 PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb
994 .getModel()).getPDBEntryAt(row).getPdbEntry();
996 pdbEntriesToView[count++] = pdbEntry;
997 SequenceI selectedSeq = (SequenceI) tbl_local_pdb
998 .getValueAt(row, refSeqColIndex);
999 selectedSeqsToView.add(selectedSeq);
1001 SequenceI[] selectedSeqs = selectedSeqsToView
1002 .toArray(new SequenceI[selectedSeqsToView.size()]);
1003 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
1006 else if (currentView == VIEWS_ENTER_ID)
1008 SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
1009 .getCmb_assSeq().getSelectedItem()).getSequence();
1010 if (userSelectedSeq != null)
1012 selectedSequence = userSelectedSeq;
1014 String pdbIdStr = txt_search.getText();
1015 PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
1016 if (pdbEntry == null)
1018 pdbEntry = new PDBEntry();
1019 if (pdbIdStr.split(":").length > 1)
1021 pdbEntry.setId(pdbIdStr.split(":")[0]);
1022 pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase());
1026 pdbEntry.setId(pdbIdStr);
1028 pdbEntry.setType(PDBEntry.Type.PDB);
1029 selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
1032 PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
1033 sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
1035 { selectedSequence });
1037 else if (currentView == VIEWS_FROM_FILE)
1039 SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
1040 .getCmb_assSeq().getSelectedItem()).getSequence();
1041 if (userSelectedSeq != null)
1043 selectedSequence = userSelectedSeq;
1045 PDBEntry fileEntry = new AssociatePdbFileWithSeq()
1046 .associatePdbWithSeq(selectedPdbFileName,
1047 DataSourceType.FILE, selectedSequence, true,
1050 sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry },
1052 { selectedSequence });
1054 SwingUtilities.invokeLater(new Runnable()
1059 closeAction(preferredHeight);
1060 mainFrame.dispose();
1065 Thread runner = new Thread(viewStruc);
1067 if (waitUntilFinished)
1069 while (sViewer == null ? runner.isAlive()
1070 : (sViewer.sview == null ? true
1071 : !sViewer.sview.hasMapping()))
1076 } catch (InterruptedException ie)
1085 * Answers a structure viewer (new or existing) configured to superimpose
1086 * added structures or not according to the user's choice
1091 StructureViewer getTargetedStructureViewer(StructureSelectionManager ssm)
1093 Object sv = targetView.getSelectedItem();
1095 return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv;
1099 * Adds PDB structures to a new or existing structure viewer
1102 * @param pdbEntriesToView
1107 private StructureViewer launchStructureViewer(
1108 StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
1109 final AlignmentPanel alignPanel, SequenceI[] sequences)
1111 long progressId = sequences.hashCode();
1112 setProgressBar(MessageManager
1113 .getString("status.launching_3d_structure_viewer"), progressId);
1114 final StructureViewer theViewer = getTargetedStructureViewer(ssm);
1115 boolean superimpose = chk_superpose.isSelected();
1116 theViewer.setSuperpose(superimpose);
1119 * remember user's choice of superimpose or not
1121 Cache.setProperty(AUTOSUPERIMPOSE,
1122 Boolean.valueOf(superimpose).toString());
1124 setProgressBar(null, progressId);
1125 if (SiftsSettings.isMapWithSifts())
1127 List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
1129 // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
1130 // real PDB ID. For moment, we can also safely do this if there is already
1131 // a known mapping between the PDBEntry and the sequence.
1132 for (SequenceI seq : sequences)
1134 PDBEntry pdbe = pdbEntriesToView[p++];
1135 if (pdbe != null && pdbe.getFile() != null)
1137 StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
1138 if (smm != null && smm.length > 0)
1140 for (StructureMapping sm : smm)
1142 if (sm.getSequence() == seq)
1149 if (seq.getPrimaryDBRefs().isEmpty())
1151 seqsWithoutSourceDBRef.add(seq);
1155 if (!seqsWithoutSourceDBRef.isEmpty())
1157 int y = seqsWithoutSourceDBRef.size();
1158 setProgressBar(MessageManager.formatMessage(
1159 "status.fetching_dbrefs_for_sequences_without_valid_refs",
1161 SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
1162 .toArray(new SequenceI[y]);
1163 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
1164 dbRefFetcher.fetchDBRefs(true);
1166 setProgressBar("Fetch complete.", progressId); // todo i18n
1169 if (pdbEntriesToView.length > 1)
1172 MessageManager.getString(
1173 "status.fetching_3d_structures_for_selected_entries"),
1175 theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel);
1179 setProgressBar(MessageManager.formatMessage(
1180 "status.fetching_3d_structures_for",
1181 pdbEntriesToView[0].getId()), progressId);
1182 theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
1184 setProgressBar(null, progressId);
1185 // remember the last viewer we used...
1186 lastTargetedView = theViewer;
1191 * Populates the combo-box used in associating manually fetched structures to
1192 * a unique sequence when more than one sequence selection is made.
1195 protected void populateCmbAssociateSeqOptions(
1196 JComboBox<AssociateSeqOptions> cmb_assSeq,
1197 JLabel lbl_associateSeq)
1199 cmb_assSeq.removeAllItems();
1201 new AssociateSeqOptions("-Select Associated Seq-", null));
1202 lbl_associateSeq.setVisible(false);
1203 if (selectedSequences.length > 1)
1205 for (SequenceI seq : selectedSequences)
1207 cmb_assSeq.addItem(new AssociateSeqOptions(seq));
1212 String seqName = selectedSequence.getDisplayId(false);
1213 seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
1214 lbl_associateSeq.setText(seqName);
1215 lbl_associateSeq.setVisible(true);
1216 cmb_assSeq.setVisible(false);
1220 protected boolean isStructuresDiscovered()
1222 return discoveredStructuresSet != null
1223 && !discoveredStructuresSet.isEmpty();
1226 protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes
1228 // Doing a search for "1" or "1c" is valuable?
1229 // Those work but are enormously slow.
1232 protected void txt_search_ActionPerformed()
1234 String text = txt_search.getText().trim();
1235 if (text.length() >= PDB_ID_MIN)
1242 errorWarning.setLength(0);
1243 isValidPBDEntry = false;
1244 if (text.length() > 0)
1246 // TODO move this pdb id search into the PDB specific
1248 // for moment, it will work fine as is because it is self-contained
1249 String searchTerm = text.toLowerCase();
1250 searchTerm = searchTerm.split(":")[0];
1251 // System.out.println(">>>>> search term : " + searchTerm);
1252 List<FTSDataColumnI> wantedFields = new ArrayList<>();
1253 FTSRestRequest pdbRequest = new FTSRestRequest();
1254 pdbRequest.setAllowEmptySeq(false);
1255 pdbRequest.setResponseSize(1);
1256 pdbRequest.setFieldToSearchBy("(pdb_id:");
1257 pdbRequest.setWantedFields(wantedFields);
1258 pdbRequest.setSearchTerm(searchTerm + ")");
1259 pdbRequest.setAssociatedSequence(selectedSequence);
1260 FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance();
1261 wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
1262 FTSRestResponse resultList;
1265 resultList = pdbRestClient.executeRequest(pdbRequest);
1266 } catch (Exception e)
1268 errorWarning.append(e.getMessage());
1272 validateSelections();
1274 if (resultList.getSearchSummary() != null
1275 && resultList.getSearchSummary().size() > 0)
1277 isValidPBDEntry = true;
1280 validateSelections();
1286 protected void tabRefresh()
1288 if (selectedSequences != null)
1290 Thread refreshThread = new Thread(new Runnable()
1295 fetchStructuresMetaData();
1296 // populateFilterComboBox(true, cachedPDBExists);
1299 ((FilterOption) cmb_filterOption.getSelectedItem())
1303 refreshThread.start();
1307 public class PDBEntryTableModel extends AbstractTableModel
1309 String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type",
1312 private List<CachedPDB> pdbEntries;
1314 public PDBEntryTableModel(List<CachedPDB> pdbEntries)
1316 this.pdbEntries = new ArrayList<>(pdbEntries);
1320 public String getColumnName(int columnIndex)
1322 return columns[columnIndex];
1326 public int getRowCount()
1328 return pdbEntries.size();
1332 public int getColumnCount()
1334 return columns.length;
1338 public boolean isCellEditable(int row, int column)
1344 public Object getValueAt(int rowIndex, int columnIndex)
1346 Object value = "??";
1347 CachedPDB entry = pdbEntries.get(rowIndex);
1348 switch (columnIndex)
1351 value = entry.getSequence();
1354 value = entry.getQualifiedId();
1357 value = entry.getPdbEntry().getChainCode() == null ? "_"
1358 : entry.getPdbEntry().getChainCode();
1361 value = entry.getPdbEntry().getType();
1364 value = entry.getPdbEntry().getFile();
1371 public Class<?> getColumnClass(int columnIndex)
1373 return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
1376 public CachedPDB getPDBEntryAt(int row)
1378 return pdbEntries.get(row);
1383 private class CachedPDB
1385 private SequenceI sequence;
1387 private PDBEntry pdbEntry;
1389 public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
1391 this.sequence = sequence;
1392 this.pdbEntry = pdbEntry;
1395 public String getQualifiedId()
1397 if (pdbEntry.hasProvider())
1399 return pdbEntry.getProvider() + ":" + pdbEntry.getId();
1401 return pdbEntry.toString();
1404 public SequenceI getSequence()
1409 public PDBEntry getPdbEntry()
1416 private IProgressIndicator progressBar;
1419 public void setProgressBar(String message, long id)
1421 progressBar.setProgressBar(message, id);
1425 public void registerHandler(long id, IProgressIndicatorHandler handler)
1427 progressBar.registerHandler(id, handler);
1431 public boolean operationInProgress()
1433 return progressBar.operationInProgress();
1436 public JalviewStructureDisplayI getOpenedStructureViewer()
1438 return sViewer == null ? null : sViewer.sview;
1442 protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
1444 data.setDocFieldPrefs(newPrefs);
1450 * @return true when all initialisation threads have finished and dialog is
1453 public boolean isDialogVisible()
1455 return mainFrame != null && data != null && cmb_filterOption != null
1456 && mainFrame.isVisible()
1457 && cmb_filterOption.getSelectedItem() != null;