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