merge
[jalview.git] / src / jalview / gui / StructureChooser.java
1 /*
2
3  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
4  * Copyright (C) 2014 The Jalview Authors
5  * 
6  * This file is part of Jalview.
7  * 
8  * Jalview is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License 
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *  
13  * Jalview is distributed in the hope that it will be useful, but 
14  * WITHOUT ANY WARRANTY; without even the implied warranty 
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
16  * PURPOSE.  See the GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
20  * The Jalview Authors are detailed in the 'AUTHORS' file.
21  */
22
23 package jalview.gui;
24
25 import jalview.datamodel.DBRefEntry;
26 import jalview.datamodel.PDBEntry;
27 import jalview.datamodel.SequenceI;
28 import jalview.jbgui.GStructureChooser;
29 import jalview.jbgui.PDBDocFieldPreferences;
30 import jalview.structure.StructureSelectionManager;
31 import jalview.util.MessageManager;
32 import jalview.ws.dbsources.PDBRestClient;
33 import jalview.ws.dbsources.PDBRestClient.PDBDocField;
34 import jalview.ws.uimodel.PDBRestRequest;
35 import jalview.ws.uimodel.PDBRestResponse;
36 import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
37
38 import java.awt.event.ItemEvent;
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.HashSet;
42 import java.util.Hashtable;
43 import java.util.LinkedHashSet;
44 import java.util.List;
45
46 import javax.swing.JCheckBox;
47 import javax.swing.JComboBox;
48 import javax.swing.JLabel;
49 import javax.swing.JOptionPane;
50 import javax.swing.table.DefaultTableModel;
51
52 /**
53  * Provides the behaviors for the Structure chooser Panel
54  * 
55  * @author tcnofoegbu
56  *
57  */
58 @SuppressWarnings("serial")
59 public class StructureChooser extends GStructureChooser
60 {
61   private boolean structuresDiscovered = false;
62
63   private SequenceI selectedSequence;
64
65   private SequenceI[] selectedSequences;
66
67   private IProgressIndicator progressIndicator;
68
69   private Collection<PDBResponseSummary> discoveredStructuresSet;
70
71   private PDBRestRequest lastPdbRequest;
72
73   private PDBRestClient pdbRestCleint;
74
75   private String selectedPdbFileName;
76
77   private boolean isValidPBDEntry;
78
79   private static Hashtable<String, PDBEntry> cachedEntryMap;
80
81   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
82           AlignmentPanel ap)
83   {
84     this.ap = ap;
85     this.selectedSequence = selectedSeq;
86     this.selectedSequences = selectedSeqs;
87     this.progressIndicator = (ap == null) ? null : ap.alignFrame;
88     init();
89   }
90
91   /**
92    * Initializes parameters used by the Structure Chooser Panel
93    */
94   public void init()
95   {
96     Thread discoverPDBStructuresThread = new Thread(new Runnable()
97     {
98       @Override
99       public void run()
100       {
101         long startTime = System.currentTimeMillis();
102         updateProgressIndicator(MessageManager
103                 .getString("status.loading_cached_pdb_entries"), startTime);
104         loadLocalCachedPDBEntries();
105         updateProgressIndicator(null, startTime);
106         updateProgressIndicator(MessageManager
107                 .getString("status.searching_for_pdb_structures"),
108                 startTime);
109         fetchStructuresMetaData();
110         populateFilterComboBox();
111         updateProgressIndicator(null, startTime);
112         mainFrame.setVisible(true);
113         updateCurrentView();
114       }
115     });
116     discoverPDBStructuresThread.start();
117   }
118
119   /**
120    * Updates the progress indicator with the specified message
121    * 
122    * @param message
123    *          displayed message for the operation
124    * @param id
125    *          unique handle for this indicator
126    */
127   public void updateProgressIndicator(String message, long id)
128   {
129     if (progressIndicator != null)
130     {
131       progressIndicator.setProgressBar(message, id);
132     }
133   }
134
135   /**
136    * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
137    * selection group
138    */
139   public void fetchStructuresMetaData()
140   {
141     long startTime = System.currentTimeMillis();
142     Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
143             .getStructureSummaryFields();
144
145     discoveredStructuresSet = new LinkedHashSet<PDBResponseSummary>();
146     HashSet<String> errors = new HashSet<String>();
147     for (SequenceI seq : selectedSequences)
148     {
149       PDBRestRequest pdbRequest = new PDBRestRequest();
150       pdbRequest.setAllowEmptySeq(false);
151       pdbRequest.setResponseSize(500);
152       pdbRequest.setFieldToSearchBy("(text:");
153       pdbRequest.setWantedFields(wantedFields);
154       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
155       pdbRequest.setAssociatedSequence(seq);
156       pdbRestCleint = new PDBRestClient();
157       PDBRestResponse resultList;
158       try
159       {
160         resultList = pdbRestCleint.executeRequest(pdbRequest);
161       } catch (Exception e)
162       {
163         e.printStackTrace();
164         errors.add(e.getMessage());
165         continue;
166       }
167       lastPdbRequest = pdbRequest;
168       if (resultList.getSearchSummary() != null
169               && !resultList.getSearchSummary().isEmpty())
170       {
171         discoveredStructuresSet.addAll(resultList.getSearchSummary());
172         updateSequencePDBEntries(seq, resultList.getSearchSummary());
173       }
174     }
175
176     int noOfStructuresFound = 0;
177     String totalTime = (System.currentTimeMillis() - startTime)
178             + " milli secs";
179     if (discoveredStructuresSet != null
180             && !discoveredStructuresSet.isEmpty())
181     {
182       tbl_summary.setModel(PDBRestResponse.getTableModel(lastPdbRequest,
183               discoveredStructuresSet));
184       structuresDiscovered = true;
185       noOfStructuresFound = discoveredStructuresSet.size();
186       mainFrame.setTitle("Structure Chooser - " + noOfStructuresFound
187               + " Found (" + totalTime + ")");
188     }
189     else
190     {
191       mainFrame.setTitle("Structure Chooser - Manual association");
192       if (errors.size() > 0)
193       {
194         StringBuilder errorMsg = new StringBuilder();
195         // "Operation was unsuccessful due to the following: \n");
196         for (String error : errors)
197         {
198           errorMsg.append(error).append("\n");
199         }
200         JOptionPane.showMessageDialog(this, errorMsg.toString(),
201                 "PDB Web-service Error", JOptionPane.ERROR_MESSAGE);
202       }
203     }
204   }
205
206   public void loadLocalCachedPDBEntries()
207   {
208     DefaultTableModel tableModel = new DefaultTableModel()
209     {
210       @Override
211       public boolean isCellEditable(int row, int column)
212       {
213         return false;
214       }
215     };
216     tableModel.addColumn("Ref Sequence");
217     tableModel.addColumn("PDB Id");
218     tableModel.addColumn("Chain");
219     tableModel.addColumn("Type");
220     tableModel.addColumn("File");
221     cachedEntryMap = new Hashtable<String, PDBEntry>();
222     for (SequenceI seq : selectedSequences)
223     {
224       if (seq.getDatasetSequence() != null
225               && seq.getDatasetSequence().getPDBId() != null)
226       {
227         for (PDBEntry pdbEntry : seq.getDatasetSequence().getPDBId())
228         {
229
230           String chain = pdbEntry.getChainCode() == null ? "_" : pdbEntry
231                   .getChainCode();
232           Object[] pdbEntryRowData = new Object[]
233           { seq, pdbEntry.getId(), chain, pdbEntry.getType(),
234               pdbEntry.getFile() };
235           if (pdbEntry.getFile() != null)
236           {
237             tableModel.addRow(pdbEntryRowData);
238           }
239           cachedEntryMap.put(pdbEntry.getId().toLowerCase(), pdbEntry);
240         }
241       }
242     }
243     tbl_local_pdb.setModel(tableModel);
244   }
245
246   /**
247    * Update the PDBEntry for a given sequence with values retrieved from
248    * PDBResponseSummary
249    * 
250    * @param seq
251    *          the Sequence to update its DBRef entry
252    * @param responseSummaries
253    *          a collection of PDBResponseSummary
254    */
255   public void updateSequencePDBEntries(SequenceI seq,
256           Collection<PDBResponseSummary> responseSummaries)
257   {
258     for (PDBResponseSummary response : responseSummaries)
259     {
260       String pdbIdStr = response.getPdbId();
261       PDBEntry pdbEntry = cachedEntryMap.get(pdbIdStr.toLowerCase());
262       if (pdbEntry == null)
263       {
264         pdbEntry = new PDBEntry();
265         pdbEntry.setId(pdbIdStr);
266         pdbEntry.setType(PDBEntry.Type.PDB);
267       }
268       seq.getDatasetSequence().addPDBId(pdbEntry);
269     }
270   }
271
272   /**
273    * Builds a query string for a given sequences using its DBRef entries
274    * 
275    * @param seq
276    *          the sequences to build a query for
277    * @return the built query string
278    */
279
280   public static String buildQuery(SequenceI seq)
281   {
282     HashSet<String> seqRefs = new LinkedHashSet<String>();
283     String seqName = seq.getName();
284     String[] names = seqName.toLowerCase().split("\\|");
285     for (String name : names)
286     {
287       // System.out.println("Found name : " + name);
288       name.trim();
289       if (isValidSeqName(name))
290       {
291         seqRefs.add(name);
292       }
293     }
294
295     if (seq.getPDBId() != null)
296     {
297       for (PDBEntry entry : seq.getPDBId())
298       {
299         if (isValidSeqName(entry.getId()))
300         {
301           seqRefs.add(entry.getId());
302         }
303       }
304     }
305
306     if (seq.getDBRef() != null && seq.getDBRef().length != 0)
307     {
308       int count = 0;
309       for (DBRefEntry dbRef : seq.getDBRef())
310       {
311         if (isValidSeqName(getDBRefId(dbRef)))
312         {
313           seqRefs.add(getDBRefId(dbRef));
314         }
315         ++count;
316         if (count > 10)
317         {
318           break;
319         }
320       }
321     }
322
323     StringBuilder queryBuilder = new StringBuilder();
324     for (String seqRef : seqRefs)
325     {
326       queryBuilder.append("text:").append(seqRef).append(" OR ");
327     }
328     int endIndex = queryBuilder.lastIndexOf(" OR ");
329     String query = queryBuilder.toString().substring(5, endIndex);
330     return query;
331   }
332
333   /**
334    * Ensures sequence ref names are not less than 3 characters and does not
335    * contain a database name
336    * 
337    * @param seqName
338    * @return
339    */
340   public static boolean isValidSeqName(String seqName)
341   {
342     // System.out.println("seqName : " + seqName);
343     String ignoreList = "pdb,uniprot,swiss-prot";
344     if (seqName.length() < 3)
345     {
346       return false;
347     }
348     if (seqName.contains(":"))
349     {
350       return false;
351     }
352     seqName = seqName.toLowerCase();
353     for (String ignoredEntry : ignoreList.split(","))
354     {
355       if (seqName.contains(ignoredEntry))
356       {
357         return false;
358       }
359     }
360     return true;
361   }
362
363   public static String getDBRefId(DBRefEntry dbRef)
364   {
365     String ref = dbRef.getAccessionId().replaceAll("GO:", "");
366     return ref;
367   }
368
369   /**
370    * Filters a given list of discovered structures based on supplied argument
371    * 
372    * @param fieldToFilterBy
373    *          the field to filter by
374    */
375   public void filterResultSet(final String fieldToFilterBy)
376   {
377     Thread filterThread = new Thread(new Runnable()
378     {
379       @Override
380       public void run()
381       {
382         long startTime = System.currentTimeMillis();
383         lbl_loading.setVisible(true);
384         Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
385                 .getStructureSummaryFields();
386         Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
387         HashSet<String> errors = new HashSet<String>();
388         for (SequenceI seq : selectedSequences)
389         {
390           PDBRestRequest pdbRequest = new PDBRestRequest();
391           pdbRequest.setAllowEmptySeq(false);
392           pdbRequest.setResponseSize(1);
393           pdbRequest.setFieldToSearchBy("(text:");
394           pdbRequest.setFieldToSortBy(fieldToFilterBy,
395                   !chk_invertFilter.isSelected());
396           pdbRequest.setSearchTerm(buildQuery(seq) + ")");
397           pdbRequest.setWantedFields(wantedFields);
398           pdbRequest.setAssociatedSequence(seq);
399           pdbRestCleint = new PDBRestClient();
400           PDBRestResponse resultList;
401           try
402           {
403             resultList = pdbRestCleint.executeRequest(pdbRequest);
404           } catch (Exception e)
405           {
406             e.printStackTrace();
407             errors.add(e.getMessage());
408             continue;
409           }
410           lastPdbRequest = pdbRequest;
411           if (resultList.getSearchSummary() != null
412                   && !resultList.getSearchSummary().isEmpty())
413           {
414             filteredResponse.addAll(resultList.getSearchSummary());
415           }
416         }
417
418         String totalTime = (System.currentTimeMillis() - startTime)
419                 + " milli secs";
420         if (!filteredResponse.isEmpty())
421         {
422           final int filterResponseCount = filteredResponse.size();
423           Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
424           reorderedStructuresSet.addAll(filteredResponse);
425           reorderedStructuresSet.addAll(discoveredStructuresSet);
426           tbl_summary.setModel(PDBRestResponse.getTableModel(
427                   lastPdbRequest, reorderedStructuresSet));
428
429           // Update table selection model here
430           tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
431
432           mainFrame.setTitle("Structure Chooser - Filter time ("
433                   + totalTime + ")");
434         }
435         else
436         {
437           mainFrame.setTitle("Structure Chooser - Filter time ("
438                   + totalTime + ")");
439           if (errors.size() > 0)
440           {
441             StringBuilder errorMsg = new StringBuilder();
442             for (String error : errors)
443             {
444               errorMsg.append(error).append("\n");
445             }
446             JOptionPane.showMessageDialog(null, errorMsg.toString(),
447                     "PDB Web-service Error", JOptionPane.ERROR_MESSAGE);
448           }
449         }
450
451         lbl_loading.setVisible(false);
452
453         validateSelections();
454       }
455     });
456     filterThread.start();
457   }
458
459   /**
460    * Handles action event for btn_pdbFromFile
461    */
462   public void pdbFromFile_actionPerformed()
463   {
464     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
465             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
466     chooser.setFileView(new jalview.io.JalviewFileView());
467     chooser.setDialogTitle(MessageManager.formatMessage(
468             "label.select_pdb_file_for", new String[]
469             { selectedSequence.getDisplayId(false) }));
470     chooser.setToolTipText(MessageManager.formatMessage(
471             "label.load_pdb_file_associate_with_sequence", new String[]
472             { selectedSequence.getDisplayId(false) }));
473
474     int value = chooser.showOpenDialog(null);
475     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
476     {
477       selectedPdbFileName = chooser.getSelectedFile().getPath();
478       jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
479       validateSelections();
480     }
481   }
482
483   /**
484    * Populates the filter combo-box options dynamically depending on discovered
485    * structures
486    */
487   protected void populateFilterComboBox()
488   {
489     if (isStructuresDiscovered())
490     {
491       cmb_filterOption.addItem(new FilterOption("Best Quality",
492               PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
493       cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
494               PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
495       cmb_filterOption.addItem(new FilterOption("Highest Resolution",
496               PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
497       cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
498               PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
499       cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
500               PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
501       cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
502               PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
503     }
504     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
505             VIEWS_ENTER_ID));
506     cmb_filterOption.addItem(new FilterOption("From File", "-",
507             VIEWS_FROM_FILE));
508     cmb_filterOption.addItem(new FilterOption("Cached PDB Entries", "-",
509             VIEWS_LOCAL_PDB));
510   }
511
512   /**
513    * Updates the displayed view based on the selected filter option
514    */
515   protected void updateCurrentView()
516   {
517     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
518             .getSelectedItem());
519     layout_switchableViews.show(pnl_switchableViews,
520             selectedFilterOpt.getView());
521     String filterTitle = mainFrame.getTitle();
522     mainFrame.setTitle(frameTitle);
523     chk_invertFilter.setVisible(false);
524     if (selectedFilterOpt.getView() == VIEWS_FILTER)
525     {
526       mainFrame.setTitle(filterTitle);
527       chk_invertFilter.setVisible(true);
528       filterResultSet(selectedFilterOpt.getValue());
529     }
530     else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
531             || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
532     {
533       mainFrame.setTitle(filterTitle);
534       idInputAssSeqPanel.loadCmbAssSeq();
535       fileChooserAssSeqPanel.loadCmbAssSeq();
536     }
537     validateSelections();
538   }
539
540   /**
541    * Validates user selection and activates the view button if all parameters
542    * are correct
543    */
544   public void validateSelections()
545   {
546     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
547             .getSelectedItem());
548     btn_view.setEnabled(false);
549     String currentView = selectedFilterOpt.getView();
550     if (currentView == VIEWS_FILTER)
551     {
552       if (tbl_summary.getSelectedRows().length > 0)
553       {
554         btn_view.setEnabled(true);
555       }
556     }
557     else if (currentView == VIEWS_LOCAL_PDB)
558     {
559       if (tbl_local_pdb.getSelectedRows().length > 0)
560       {
561         btn_view.setEnabled(true);
562       }
563     }
564     else if (currentView == VIEWS_ENTER_ID)
565     {
566       validateAssociationEnterPdb();
567     }
568     else if (currentView == VIEWS_FROM_FILE)
569     {
570       validateAssociationFromFile();
571     }
572   }
573
574   /**
575    * Validates inputs from the Manual PDB entry panel
576    */
577   public void validateAssociationEnterPdb()
578   {
579     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
580             .getCmb_assSeq().getSelectedItem();
581     lbl_pdbManualFetchStatus.setIcon(errorImage);
582     lbl_pdbManualFetchStatus.setToolTipText("");
583     if (txt_search.getText().length() > 0)
584     {
585       lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(
586               true, "No PDB entry found for \'" + txt_search.getText()
587                       + "\'"));
588     }
589
590     if (errorWarning.length() > 0)
591     {
592       lbl_pdbManualFetchStatus.setIcon(warningImage);
593       lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(
594               true, errorWarning.toString()));
595     }
596
597     if (selectedSequences.length == 1
598             || !assSeqOpt.getName().equalsIgnoreCase(
599                     "-Select Associated Seq-"))
600     {
601       txt_search.setEnabled(true);
602       if (isValidPBDEntry)
603       {
604         btn_view.setEnabled(true);
605         lbl_pdbManualFetchStatus.setToolTipText("");
606         lbl_pdbManualFetchStatus.setIcon(goodImage);
607       }
608     }
609     else
610     {
611       txt_search.setEnabled(false);
612       lbl_pdbManualFetchStatus.setIcon(errorImage);
613     }
614   }
615
616   /**
617    * Validates inputs for the manual PDB file selection options
618    */
619   public void validateAssociationFromFile()
620   {
621     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
622             .getCmb_assSeq().getSelectedItem();
623     lbl_fromFileStatus.setIcon(errorImage);
624     if (selectedSequences.length == 1
625             || (assSeqOpt != null && !assSeqOpt.getName().equalsIgnoreCase(
626                     "-Select Associated Seq-")))
627     {
628       btn_pdbFromFile.setEnabled(true);
629       if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
630       {
631         btn_view.setEnabled(true);
632         lbl_fromFileStatus.setIcon(goodImage);
633       }
634     }
635     else
636     {
637       btn_pdbFromFile.setEnabled(false);
638       lbl_fromFileStatus.setIcon(errorImage);
639     }
640   }
641
642   @Override
643   public void cmbAssSeqStateChanged()
644   {
645     validateSelections();
646   }
647
648   /**
649    * Handles the state change event for the 'filter' combo-box and 'invert'
650    * check-box
651    */
652   @Override
653   protected void stateChanged(ItemEvent e)
654   {
655     if (e.getSource() instanceof JCheckBox)
656     {
657       updateCurrentView();
658     }
659     else
660     {
661       if (e.getStateChange() == ItemEvent.SELECTED)
662       {
663         updateCurrentView();
664       }
665     }
666
667   }
668
669   /**
670    * Handles action event for btn_ok
671    */
672   @Override
673   public void ok_ActionPerformed()
674   {
675     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
676             .getSelectedItem());
677     String currentView = selectedFilterOpt.getView();
678     if (currentView == VIEWS_FILTER)
679     {
680       int pdbIdColIndex = tbl_summary.getColumn(
681               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
682       int refSeqColIndex = tbl_summary.getColumn("Ref Sequence")
683               .getModelIndex();
684       int[] selectedRows = tbl_summary.getSelectedRows();
685       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
686       int count = 0;
687       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
688       for (int summaryRow : selectedRows)
689       {
690         String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdColIndex)
691                 .toString();
692         SequenceI selectedSeq = (SequenceI) tbl_summary.getValueAt(
693                 summaryRow, refSeqColIndex);
694         selectedSeqsToView.add(selectedSeq);
695         PDBEntry pdbEntry = cachedEntryMap.get(pdbIdStr.toLowerCase());
696         if (pdbEntry == null)
697         {
698           pdbEntry = new PDBEntry();
699           pdbEntry.setId(pdbIdStr);
700           pdbEntry.setType(PDBEntry.Type.PDB);
701         }
702         pdbEntriesToView[count++] = pdbEntry;
703       }
704       SequenceI[] selectedSeqs = selectedSeqsToView
705               .toArray(new SequenceI[selectedSeqsToView.size()]);
706       launchStructureViewer(ap.getStructureSelectionManager(),
707               pdbEntriesToView, ap, selectedSeqs);
708     }
709     else if (currentView == VIEWS_LOCAL_PDB)
710     {
711       int[] selectedRows = tbl_local_pdb.getSelectedRows();
712       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
713       int count = 0;
714       int pdbIdColIndex = tbl_local_pdb.getColumn(
715               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
716       int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
717               .getModelIndex();
718       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
719       for (int row : selectedRows)
720       {
721         String entryKey = tbl_local_pdb.getValueAt(row, pdbIdColIndex)
722                 .toString().toLowerCase();
723         pdbEntriesToView[count++] = cachedEntryMap.get(entryKey);
724         SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(row,
725                 refSeqColIndex);
726         selectedSeqsToView.add(selectedSeq);
727
728       }
729       SequenceI[] selectedSeqs = selectedSeqsToView
730               .toArray(new SequenceI[selectedSeqsToView.size()]);
731       launchStructureViewer(ap.getStructureSelectionManager(),
732               pdbEntriesToView, ap, selectedSeqs);
733     }
734     else if (currentView == VIEWS_ENTER_ID)
735     {
736       SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
737               .getCmb_assSeq().getSelectedItem()).getSequence();
738       if (userSelectedSeq != null)
739       {
740         selectedSequence = userSelectedSeq;
741       }
742
743       String pdbIdStr = txt_search.getText();
744       PDBEntry pdbEntry = cachedEntryMap.get(pdbIdStr.toLowerCase());
745       if (pdbEntry == null)
746       {
747         pdbEntry = new PDBEntry();
748         pdbEntry.setId(txt_search.getText());
749         pdbEntry.setType(PDBEntry.Type.PDB);
750       }
751
752       selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
753       PDBEntry[] pdbEntriesToView = new PDBEntry[]
754       { pdbEntry };
755       launchStructureViewer(ap.getStructureSelectionManager(),
756               pdbEntriesToView, ap, new SequenceI[]
757               { selectedSequence });
758     }
759     else if (currentView == VIEWS_FROM_FILE)
760     {
761       SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
762               .getCmb_assSeq().getSelectedItem()).getSequence();
763       if (userSelectedSeq != null)
764       {
765         selectedSequence = userSelectedSeq;
766       }
767       PDBEntry fileEntry = new AssociatePdbFileWithSeq()
768               .associatePdbWithSeq(selectedPdbFileName,
769                       jalview.io.AppletFormatAdapter.FILE,
770                       selectedSequence, true, Desktop.instance);
771
772       launchStructureViewer(ap.getStructureSelectionManager(),
773               new PDBEntry[]
774               { fileEntry }, ap, new SequenceI[]
775               { selectedSequence });
776     }
777     mainFrame.dispose();
778   }
779
780   private void launchStructureViewer(final StructureSelectionManager ssm,
781           final PDBEntry[] pdbEntriesToView,
782           final AlignmentPanel alignPanel, final SequenceI[] sequences)
783   {
784     final StructureViewer sViewer = new StructureViewer(ssm);
785     new Thread(new Runnable()
786     {
787       public void run()
788       {
789         if (pdbEntriesToView.length > 1)
790         {
791           ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
792           for (SequenceI seq : sequences)
793           {
794             seqsMap.add(new SequenceI[]
795             { seq });
796           }
797           SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
798           sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
799           // sViewer.viewStructures(pdbEntriesToView,
800           // alignPanel.av.collateForPDB(pdbEntriesToView),
801           // alignPanel);
802         }
803         else
804         {
805           sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
806         }
807       }
808     }).start();
809   }
810
811   /**
812    * Populates the combo-box used in associating manually fetched structures to
813    * a unique sequence when more than one sequence selection is made.
814    */
815   public void populateCmbAssociateSeqOptions(
816           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
817   {
818     cmb_assSeq.removeAllItems();
819     cmb_assSeq.addItem(new AssociateSeqOptions("-Select Associated Seq-",
820             null));
821     // cmb_assSeq.addItem(new AssociateSeqOptions("Auto Detect", null));
822     lbl_associateSeq.setVisible(false);
823     if (selectedSequences.length > 1)
824     {
825       for (SequenceI seq : selectedSequences)
826       {
827         cmb_assSeq.addItem(new AssociateSeqOptions(seq));
828       }
829     }
830     else
831     {
832       String seqName = selectedSequence.getDisplayId(false);
833       seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
834       lbl_associateSeq.setText(seqName);
835       lbl_associateSeq.setVisible(true);
836       cmb_assSeq.setVisible(false);
837     }
838   }
839
840   public boolean isStructuresDiscovered()
841   {
842     return structuresDiscovered;
843   }
844
845   public void setStructuresDiscovered(boolean structuresDiscovered)
846   {
847     this.structuresDiscovered = structuresDiscovered;
848   }
849
850   public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
851   {
852     return discoveredStructuresSet;
853   }
854
855   @Override
856   protected void txt_search_ActionPerformed()
857   {
858     errorWarning.setLength(0);
859     isValidPBDEntry = false;
860     if (txt_search.getText().length() > 0)
861     {
862       List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
863       wantedFields.add(PDBDocField.PDB_ID);
864       PDBRestRequest pdbRequest = new PDBRestRequest();
865       pdbRequest.setAllowEmptySeq(false);
866       pdbRequest.setResponseSize(1);
867       pdbRequest.setFieldToSearchBy("(pdb_id:");
868       pdbRequest.setWantedFields(wantedFields);
869       pdbRequest.setSearchTerm(txt_search.getText() + ")");
870       pdbRequest.setAssociatedSequence(selectedSequence);
871       pdbRestCleint = new PDBRestClient();
872       PDBRestResponse resultList;
873       try
874       {
875         resultList = pdbRestCleint.executeRequest(pdbRequest);
876       } catch (Exception e)
877       {
878         errorWarning.append(e.getMessage());
879         return;
880       } finally
881       {
882         validateSelections();
883       }
884       if (resultList.getSearchSummary() != null
885               && resultList.getSearchSummary().size() > 0)
886       {
887         isValidPBDEntry = true;
888       }
889     }
890     validateSelections();
891   }
892
893   @Override
894   public void tabRefresh()
895   {
896     if (selectedSequences != null)
897     {
898       Thread refreshThread = new Thread(new Runnable()
899       {
900         @Override
901         public void run()
902         {
903           fetchStructuresMetaData();
904           filterResultSet(((FilterOption) cmb_filterOption
905                   .getSelectedItem()).getValue());
906         }
907       });
908       refreshThread.start();
909     }
910   }
911
912 }