79f25e2d277879e67869084f9fd25c29edfbafaf
[jalview.git] / src / jalview / gui / SequenceFetcher.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 package jalview.gui;
22
23 import jalview.api.FeatureSettingsModelI;
24 import jalview.bin.Cache;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.DBRefEntry;
27 import jalview.datamodel.SequenceI;
28 import jalview.fts.core.GFTSPanel;
29 import jalview.fts.service.pdb.PDBFTSPanel;
30 import jalview.fts.service.uniprot.UniprotFTSPanel;
31 import jalview.io.FileFormatI;
32 import jalview.io.gff.SequenceOntologyI;
33 import jalview.util.DBRefUtils;
34 import jalview.util.MessageManager;
35 import jalview.util.Platform;
36 import jalview.ws.seqfetcher.DbSourceProxy;
37
38 import java.awt.BorderLayout;
39 import java.awt.Font;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.event.KeyAdapter;
43 import java.awt.event.KeyEvent;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.HashSet;
47 import java.util.Iterator;
48 import java.util.List;
49
50 import javax.swing.JButton;
51 import javax.swing.JCheckBox;
52 import javax.swing.JComboBox;
53 import javax.swing.JInternalFrame;
54 import javax.swing.JLabel;
55 import javax.swing.JPanel;
56 import javax.swing.JScrollPane;
57 import javax.swing.JTextArea;
58 import javax.swing.SwingConstants;
59
60 /**
61  * A panel where the use may choose a database source, and enter one or more
62  * accessions, to retrieve entries from the database.
63  * <p>
64  * If the selected source is Uniprot or PDB, a free text search panel is opened
65  * instead to perform the search and selection.
66  */
67 public class SequenceFetcher extends JPanel implements Runnable
68 {
69   private static jalview.ws.SequenceFetcher sfetch = null;
70
71   JLabel exampleAccession;
72
73   JComboBox<String> database;
74
75   JCheckBox replacePunctuation;
76
77   JButton okBtn;
78
79   JButton exampleBtn;
80
81   JButton closeBtn;
82
83   JButton backBtn;
84
85   JTextArea textArea;
86
87   JInternalFrame frame;
88
89   IProgressIndicator guiWindow;
90
91   AlignFrame alignFrame;
92
93   GFTSPanel parentSearchPanel;
94
95   IProgressIndicator progressIndicator;
96
97   volatile boolean _isConstructing = false;
98
99   /**
100    * Returns the shared instance of the SequenceFetcher client
101    * 
102    * @return
103    */
104   public static jalview.ws.SequenceFetcher getSequenceFetcherSingleton()
105   {
106     if (sfetch == null)
107     {
108       sfetch = new jalview.ws.SequenceFetcher();
109     }
110     return sfetch;
111   }
112
113   /**
114    * Constructor given a client to receive any status or progress messages
115    * (currently either the Desktop, or an AlignFrame panel)
116    * 
117    * @param guiIndic
118    */
119   public SequenceFetcher(IProgressIndicator guiIndic)
120   {
121     this(guiIndic, null, null);
122   }
123
124   /**
125    * Constructor with specified database and accession(s) to retrieve
126    * 
127    * @param guiIndic
128    * @param selectedDb
129    * @param queryString
130    */
131   public SequenceFetcher(IProgressIndicator guiIndic,
132           final String selectedDb, final String queryString)
133   {
134     this.progressIndicator = guiIndic;
135     getSequenceFetcherSingleton();
136     this.guiWindow = progressIndicator;
137
138     if (progressIndicator instanceof AlignFrame)
139     {
140       alignFrame = (AlignFrame) progressIndicator;
141     }
142
143     jbInit(selectedDb);
144     textArea.setText(queryString);
145
146     frame = new JInternalFrame();
147     frame.setContentPane(this);
148     int height = Platform.isAMacAndNotJS() ? 240 : 180;
149     Desktop.addInternalFrame(frame, getFrameTitle(), true, 400, height);
150   }
151
152   private String getFrameTitle()
153   {
154     return ((alignFrame == null)
155             ? MessageManager.getString("label.new_sequence_fetcher")
156             : MessageManager
157                     .getString("label.additional_sequence_fetcher"));
158   }
159
160   private void jbInit(String selectedDb)
161   {
162     this.setLayout(new BorderLayout());
163
164     database = new JComboBox<>();
165     database.setFont(JvSwingUtils.getLabelFont());
166     database.setPrototypeDisplayValue("ENSEMBLGENOMES   ");
167     String[] sources = new jalview.ws.SequenceFetcher().getSupportedDb();
168     Arrays.sort(sources, String.CASE_INSENSITIVE_ORDER);
169     database.addItem(MessageManager.getString("action.select_ddbb"));
170     for (String source : sources)
171     {
172       database.addItem(source);
173     }
174     database.setSelectedItem(selectedDb);
175     if (database.getSelectedIndex() == -1)
176     {
177       database.setSelectedIndex(0);
178     }
179     database.setMaximumRowCount(database.getItemCount());
180     database.addActionListener(new ActionListener()
181     {
182       @Override
183       public void actionPerformed(ActionEvent e)
184       {
185         String currentSelection = (String) database.getSelectedItem();
186         updateExampleQuery(currentSelection);
187
188         if ("pdb".equalsIgnoreCase(currentSelection))
189         {
190           frame.dispose();
191           new PDBFTSPanel(SequenceFetcher.this);
192         }
193         else if ("uniprot".equalsIgnoreCase(currentSelection))
194         {
195           frame.dispose();
196           new UniprotFTSPanel(SequenceFetcher.this);
197         }
198         else
199         {
200           otherSourceAction();
201         }
202       }
203     });
204
205     exampleAccession = new JLabel("");
206     exampleAccession.setFont(new Font("Verdana", Font.BOLD, 11));
207     JLabel jLabel1 = new JLabel(MessageManager
208             .getString("label.separate_multiple_accession_ids"));
209     jLabel1.setFont(new Font("Verdana", Font.ITALIC, 11));
210     jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
211
212     replacePunctuation = new JCheckBox(
213             MessageManager.getString("label.replace_commas_semicolons"));
214     replacePunctuation.setHorizontalAlignment(SwingConstants.LEFT);
215     replacePunctuation.setFont(new Font("Verdana", Font.ITALIC, 11));
216     okBtn = new JButton(MessageManager.getString("action.ok"));
217     okBtn.addActionListener(new ActionListener()
218     {
219       @Override
220       public void actionPerformed(ActionEvent e)
221       {
222         ok_actionPerformed();
223       }
224     });
225     JButton clear = new JButton(MessageManager.getString("action.clear"));
226     clear.addActionListener(new ActionListener()
227     {
228       @Override
229       public void actionPerformed(ActionEvent e)
230       {
231         clear_actionPerformed();
232       }
233     });
234
235     exampleBtn = new JButton(MessageManager.getString("label.example"));
236     exampleBtn.addActionListener(new ActionListener()
237     {
238       @Override
239       public void actionPerformed(ActionEvent e)
240       {
241         example_actionPerformed();
242       }
243     });
244     closeBtn = new JButton(MessageManager.getString("action.cancel"));
245     closeBtn.addActionListener(new ActionListener()
246     {
247       @Override
248       public void actionPerformed(ActionEvent e)
249       {
250         close_actionPerformed(e);
251       }
252     });
253     backBtn = new JButton(MessageManager.getString("action.back"));
254     backBtn.addActionListener(new ActionListener()
255     {
256       @Override
257       public void actionPerformed(ActionEvent e)
258       {
259         parentSearchPanel.btn_back_ActionPerformed();
260       }
261     });
262     // back not visible unless embedded
263     backBtn.setVisible(false);
264
265     textArea = new JTextArea();
266     textArea.setFont(JvSwingUtils.getLabelFont());
267     textArea.setLineWrap(true);
268     textArea.addKeyListener(new KeyAdapter()
269     {
270       @Override
271       public void keyPressed(KeyEvent e)
272       {
273         if (e.getKeyCode() == KeyEvent.VK_ENTER)
274         {
275           ok_actionPerformed();
276         }
277       }
278     });
279
280     JPanel actionPanel = new JPanel();
281     actionPanel.add(backBtn);
282     actionPanel.add(exampleBtn);
283     actionPanel.add(clear);
284     actionPanel.add(okBtn);
285     actionPanel.add(closeBtn);
286
287     JPanel databasePanel = new JPanel();
288     databasePanel.setLayout(new BorderLayout());
289     databasePanel.add(database, BorderLayout.NORTH);
290     databasePanel.add(exampleAccession, BorderLayout.CENTER);
291     JPanel jPanel2a = new JPanel(new BorderLayout());
292     jPanel2a.add(jLabel1, BorderLayout.NORTH);
293     jPanel2a.add(replacePunctuation, BorderLayout.SOUTH);
294     databasePanel.add(jPanel2a, BorderLayout.SOUTH);
295
296     JPanel idsPanel = new JPanel();
297     idsPanel.setLayout(new BorderLayout(0, 5));
298     JScrollPane jScrollPane1 = new JScrollPane();
299     jScrollPane1.getViewport().add(textArea);
300     idsPanel.add(jScrollPane1, BorderLayout.CENTER);
301
302     this.add(actionPanel, BorderLayout.SOUTH);
303     this.add(idsPanel, BorderLayout.CENTER);
304     this.add(databasePanel, BorderLayout.NORTH);
305   }
306
307   /**
308    * Answers a semi-colon-delimited string with the example query or queries for
309    * the selected database
310    * 
311    * @param db
312    * @return
313    */
314   protected String getExampleQueries(String db)
315   {
316     StringBuilder sb = new StringBuilder();
317     HashSet<String> hs = new HashSet<>();
318     for (DbSourceProxy dbs : sfetch.getSourceProxy(db))
319     {
320       String tq = dbs.getTestQuery();
321       if (hs.add(tq)) // not a duplicate source
322       {
323         if (sb.length() > 0)
324         {
325           sb.append(";");
326         }
327         sb.append(tq);
328       }
329     }
330     return sb.toString();
331   }
332
333   /**
334    * Action on selecting a database other than Uniprot or PDB is to enable or
335    * disable 'Replace commas', and await input in the query field
336    */
337   protected void otherSourceAction()
338   {
339     try
340     {
341       String eq = exampleAccession.getText();
342       // TODO this should be a property of the SequenceFetcher whether commas
343       // are allowed in the IDs...
344
345       boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
346       replacePunctuation.setEnabled(enablePunct);
347
348     } catch (Exception ex)
349     {
350       exampleAccession.setText("");
351       replacePunctuation.setEnabled(true);
352     }
353     repaint();
354   }
355
356   /**
357    * Sets the text of the example query to incorporate the example accession
358    * provided by the selected database source
359    * 
360    * @param selectedDatabase
361    * @return
362    */
363   protected String updateExampleQuery(String selectedDatabase)
364   {
365     String eq = getExampleQueries(selectedDatabase);
366     exampleAccession.setText(MessageManager
367             .formatMessage("label.example_query_param", new String[]
368             { eq }));
369     return eq;
370   }
371
372   /**
373    * Action on clicking the 'Example' button is to write the example accession
374    * as the query text field value
375    */
376   protected void example_actionPerformed()
377   {
378     String eq = getExampleQueries((String) database.getSelectedItem());
379     textArea.setText(eq);
380     repaint();
381   }
382
383   /**
384    * Clears the query input field
385    */
386   protected void clear_actionPerformed()
387   {
388     textArea.setText("");
389     repaint();
390   }
391
392   /**
393    * Action on Close button is to close this frame, and also (if it is embedded
394    * in a search panel) to close the search panel
395    * 
396    * @param e
397    */
398   protected void close_actionPerformed(ActionEvent e)
399   {
400     try
401     {
402       frame.setClosed(true);
403       if (parentSearchPanel != null)
404       {
405         parentSearchPanel.btn_cancel_ActionPerformed();
406       }
407     } catch (Exception ex)
408     {
409     }
410   }
411
412   /**
413    * Action on OK is to start the fetch for entered accession(s)
414    */
415   public void ok_actionPerformed()
416   {
417     /*
418      * tidy inputs and check there is something to search for
419      */
420     String text = textArea.getText();
421     if (replacePunctuation.isEnabled() && replacePunctuation.isSelected())
422     {
423       text = text.replace(",", ";");
424     }
425     text = text.replaceAll("(\\s|[,; ])+", ";");
426     textArea.setText(text);
427     if (text.isEmpty())
428     {
429       // todo i18n
430       showErrorMessage(
431               "Please enter a (semi-colon separated list of) database id(s)");
432       resetDialog();
433       return;
434     }
435     exampleBtn.setEnabled(false);
436     textArea.setEnabled(false);
437     okBtn.setEnabled(false);
438     closeBtn.setEnabled(false);
439     backBtn.setEnabled(false);
440
441     Thread worker = new Thread(this);
442     worker.start();
443   }
444
445   private void resetDialog()
446   {
447     exampleBtn.setEnabled(true);
448     textArea.setEnabled(true);
449     okBtn.setEnabled(true);
450     closeBtn.setEnabled(true);
451     backBtn.setEnabled(parentSearchPanel != null);
452   }
453
454   @Override
455   public void run()
456   {
457     boolean addToLast = false;
458     List<String> aresultq = new ArrayList<>();
459     List<String> presultTitle = new ArrayList<>();
460     List<AlignmentI> presult = new ArrayList<>();
461     List<AlignmentI> aresult = new ArrayList<>();
462     List<DbSourceProxy> sources = sfetch
463             .getSourceProxy((String) database.getSelectedItem());
464     Iterator<DbSourceProxy> proxies = sources.iterator();
465     String[] qries;
466     List<String> nextFetch = Arrays
467             .asList(qries = textArea.getText().split(";"));
468     Iterator<String> en = Arrays.asList(new String[0]).iterator();
469     int nqueries = qries.length;
470
471     FeatureSettingsModelI preferredFeatureColours = null;
472     while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
473     {
474       if (!en.hasNext() && nextFetch.size() > 0)
475       {
476         en = nextFetch.iterator();
477         nqueries = nextFetch.size();
478         // save the remaining queries in the original array
479         qries = nextFetch.toArray(new String[nqueries]);
480         nextFetch = new ArrayList<>();
481       }
482
483       DbSourceProxy proxy = proxies.next();
484       try
485       {
486         // update status
487         guiWindow.setProgressBar(MessageManager.formatMessage(
488                 "status.fetching_sequence_queries_from", new String[]
489                 { Integer.valueOf(nqueries).toString(),
490                     proxy.getDbName() }),
491                 Thread.currentThread().hashCode());
492         if (proxy.getMaximumQueryCount() == 1)
493         {
494           /*
495            * proxy only handles one accession id at a time
496            */
497           while (en.hasNext())
498           {
499             String acc = en.next();
500             if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
501             {
502               nextFetch.add(acc);
503             }
504           }
505         }
506         else
507         {
508           /*
509            * proxy can fetch multiple accessions at one time
510            */
511           fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
512         }
513       } catch (Exception e)
514       {
515         showErrorMessage("Error retrieving " + textArea.getText() + " from "
516                 + database.getSelectedItem());
517         // error
518         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
519         System.err.println("Retrieval failed for source ='"
520                 + database.getSelectedItem() + "' and query\n'"
521                 + textArea.getText() + "'\n");
522         e.printStackTrace();
523       } catch (OutOfMemoryError e)
524       {
525         showErrorMessage("Out of Memory when retrieving "
526                 + textArea.getText() + " from " + database.getSelectedItem()
527                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
528         e.printStackTrace();
529       } catch (Error e)
530       {
531         showErrorMessage("Serious Error retrieving " + textArea.getText()
532                 + " from " + database.getSelectedItem());
533         e.printStackTrace();
534       }
535
536       // Stack results ready for opening in alignment windows
537       if (aresult != null && aresult.size() > 0)
538       {
539         FeatureSettingsModelI proxyColourScheme = proxy
540                 .getFeatureColourScheme();
541         if (proxyColourScheme != null)
542         {
543           preferredFeatureColours = proxyColourScheme;
544         }
545
546         AlignmentI ar = null;
547         if (proxy.isAlignmentSource())
548         {
549           addToLast = false;
550           // new window for each result
551           while (aresult.size() > 0)
552           {
553             presult.add(aresult.remove(0));
554             presultTitle.add(
555                     aresultq.remove(0) + " " + getDefaultRetrievalTitle());
556           }
557         }
558         else
559         {
560           String titl = null;
561           if (addToLast && presult.size() > 0)
562           {
563             ar = presult.remove(presult.size() - 1);
564             titl = presultTitle.remove(presultTitle.size() - 1);
565           }
566           // concatenate all results in one window
567           while (aresult.size() > 0)
568           {
569             if (ar == null)
570             {
571               ar = aresult.remove(0);
572             }
573             else
574             {
575               ar.append(aresult.remove(0));
576             }
577           }
578           addToLast = true;
579           presult.add(ar);
580           presultTitle.add(titl);
581         }
582       }
583       guiWindow.setProgressBar(
584               MessageManager.getString("status.finshed_querying"),
585               Thread.currentThread().hashCode());
586     }
587     guiWindow
588             .setProgressBar(
589                     (presult.size() > 0)
590                             ? MessageManager
591                                     .getString("status.parsing_results")
592                             : MessageManager.getString("status.processing"),
593                     Thread.currentThread().hashCode());
594     // process results
595     while (presult.size() > 0)
596     {
597       parseResult(presult.remove(0), presultTitle.remove(0), null,
598               preferredFeatureColours);
599     }
600     // only remove visual delay after we finished parsing.
601     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
602     if (nextFetch.size() > 0)
603     {
604       StringBuffer sb = new StringBuffer();
605       sb.append("Didn't retrieve the following "
606               + (nextFetch.size() == 1 ? "query"
607                       : nextFetch.size() + " queries")
608               + ": \n");
609       int l = sb.length(), lr = 0;
610       for (String s : nextFetch)
611       {
612         if (l != sb.length())
613         {
614           sb.append("; ");
615         }
616         if (lr - sb.length() > 40)
617         {
618           sb.append("\n");
619         }
620         sb.append(s);
621       }
622       showErrorMessage(sb.toString());
623     }
624     resetDialog();
625   }
626
627   /**
628    * Tries to fetch one or more accession ids from the database proxy
629    * 
630    * @param proxy
631    * @param accessions
632    *          the queries to fetch
633    * @param aresultq
634    *          a successful queries list to add to
635    * @param aresult
636    *          a list of retrieved alignments to add to
637    * @param nextFetch
638    *          failed queries are added to this list
639    * @throws Exception
640    */
641   void fetchMultipleAccessions(DbSourceProxy proxy,
642           Iterator<String> accessions, List<String> aresultq,
643           List<AlignmentI> aresult, List<String> nextFetch) throws Exception
644   {
645     StringBuilder multiacc = new StringBuilder();
646     List<String> tosend = new ArrayList<>();
647     while (accessions.hasNext())
648     {
649       String nel = accessions.next();
650       tosend.add(nel);
651       multiacc.append(nel);
652       if (accessions.hasNext())
653       {
654         multiacc.append(proxy.getAccessionSeparator());
655       }
656     }
657
658     try
659     {
660       String query = multiacc.toString();
661       AlignmentI rslt = proxy.getSequenceRecords(query);
662       if (rslt == null || rslt.getHeight() == 0)
663       {
664         // no results - pass on all queries to next source
665         nextFetch.addAll(tosend);
666       }
667       else
668       {
669         aresultq.add(query);
670         aresult.add(rslt);
671         if (tosend.size() > 1)
672         {
673           checkResultForQueries(rslt, tosend, nextFetch, proxy);
674         }
675       }
676     } catch (OutOfMemoryError oome)
677     {
678       new OOMWarning("fetching " + multiacc + " from "
679               + database.getSelectedItem(), oome, this);
680     }
681   }
682
683   /**
684    * Query for a single accession id via the database proxy
685    * 
686    * @param proxy
687    * @param accession
688    * @param aresultq
689    *          a list of successful queries to add to
690    * @param aresult
691    *          a list of retrieved alignments to add to
692    * @return true if the fetch was successful, else false
693    */
694   boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
695           List<String> aresultq, List<AlignmentI> aresult)
696   {
697     boolean success = false;
698     try
699     {
700       if (aresult != null)
701       {
702         try
703         {
704           // give the server a chance to breathe
705           Thread.sleep(5);
706         } catch (Exception e)
707         {
708           //
709         }
710       }
711
712       AlignmentI indres = null;
713       try
714       {
715         indres = proxy.getSequenceRecords(accession);
716       } catch (OutOfMemoryError oome)
717       {
718         new OOMWarning(
719                 "fetching " + accession + " from " + proxy.getDbName(),
720                 oome, this);
721       }
722       if (indres != null)
723       {
724         aresultq.add(accession);
725         aresult.add(indres);
726         success = true;
727       }
728     } catch (Exception e)
729     {
730       Cache.log.info("Error retrieving " + accession + " from "
731               + proxy.getDbName(), e);
732     }
733     return success;
734   }
735
736   /**
737    * Checks which of the queries were successfully retrieved by searching the
738    * DBRefs of the retrieved sequences for a match. Any not found are added to
739    * the 'nextFetch' list.
740    * 
741    * @param rslt
742    * @param queries
743    * @param nextFetch
744    * @param proxy
745    */
746   void checkResultForQueries(AlignmentI rslt, List<String> queries,
747           List<String> nextFetch, DbSourceProxy proxy)
748   {
749     SequenceI[] rs = rslt.getSequencesArray();
750
751     for (String q : queries)
752     {
753       DBRefEntry dbr = new DBRefEntry();
754       dbr.setSource(proxy.getDbSource());
755       dbr.setVersion(null);
756       String accId = proxy.getAccessionIdFromQuery(q);
757       dbr.setAccessionId(accId);
758       boolean rfound = false;
759       for (int r = 0; r < rs.length; r++)
760       {
761         if (rs[r] != null)
762         {
763           List<DBRefEntry> found = DBRefUtils.searchRefs(rs[r].getDBRefs(),
764                   accId);
765           if (!found.isEmpty())
766           {
767             rfound = true;
768             break;
769           }
770         }
771       }
772       if (!rfound)
773       {
774         nextFetch.add(q);
775       }
776     }
777   }
778
779   /**
780    * 
781    * @return a standard title for any results retrieved using the currently
782    *         selected source and settings
783    */
784   public String getDefaultRetrievalTitle()
785   {
786     return "Retrieved from " + database.getSelectedItem();
787   }
788
789   AlignmentI parseResult(AlignmentI al, String title,
790           FileFormatI currentFileFormat,
791           FeatureSettingsModelI preferredFeatureColours)
792   {
793
794     if (al != null && al.getHeight() > 0)
795     {
796       if (title == null)
797       {
798         title = getDefaultRetrievalTitle();
799       }
800       if (alignFrame == null)
801       {
802         AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
803                 AlignFrame.DEFAULT_HEIGHT);
804         if (currentFileFormat != null)
805         {
806           af.currentFileFormat = currentFileFormat;
807         }
808
809         List<SequenceI> alsqs = al.getSequences();
810         synchronized (alsqs)
811         {
812           for (SequenceI sq : alsqs)
813           {
814             if (sq.getFeatures().hasFeatures())
815             {
816               af.setShowSeqFeatures(true);
817               break;
818             }
819           }
820         }
821
822         if (preferredFeatureColours != null)
823         {
824           af.getViewport().applyFeaturesStyle(preferredFeatureColours);
825         }
826         if (Cache.getDefault("HIDE_INTRONS", true))
827         {
828           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
829         }
830         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
831                 AlignFrame.DEFAULT_HEIGHT);
832
833         af.setStatus(MessageManager
834                 .getString("label.successfully_pasted_alignment_file"));
835
836         try
837         {
838           af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
839         } catch (Exception ex)
840         {
841         }
842       }
843       else
844       {
845         alignFrame.viewport.addAlignment(al, title);
846       }
847     }
848     return al;
849   }
850
851   void showErrorMessage(final String error)
852   {
853     resetDialog();
854     javax.swing.SwingUtilities.invokeLater(new Runnable()
855     {
856       @Override
857       public void run()
858       {
859         JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
860                 MessageManager.getString("label.error_retrieving_data"),
861                 JvOptionPane.WARNING_MESSAGE);
862       }
863     });
864   }
865
866   public IProgressIndicator getProgressIndicator()
867   {
868     return progressIndicator;
869   }
870
871   public void setProgressIndicator(IProgressIndicator progressIndicator)
872   {
873     this.progressIndicator = progressIndicator;
874   }
875
876   /**
877    * Hide this panel (on clicking the database button to open the database
878    * chooser)
879    */
880   void hidePanel()
881   {
882     frame.setVisible(false);
883   }
884
885   public void setQuery(String ids)
886   {
887     textArea.setText(ids);
888   }
889
890   /**
891    * Called to modify the search panel for embedding as an alternative tab of a
892    * free text search panel. The database choice list is hidden (since the
893    * choice has been made), and a Back button is made visible (which reopens the
894    * Sequence Fetcher panel).
895    * 
896    * @param parentPanel
897    */
898   public void embedIn(GFTSPanel parentPanel)
899   {
900     database.setVisible(false);
901     backBtn.setVisible(true);
902     parentSearchPanel = parentPanel;
903   }
904 }