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