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