JAL-3144 SequenceFetcher without JDatabaseTree
[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.isAMac() ? 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           new PDBFTSPanel(SequenceFetcher.this);
191           frame.dispose();
192         }
193         else if ("uniprot".equalsIgnoreCase(currentSelection))
194         {
195           new UniprotFTSPanel(SequenceFetcher.this);
196           frame.dispose();
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.CENTER);
211
212     replacePunctuation = new JCheckBox(
213             MessageManager.getString("label.replace_commas_semicolons"));
214     replacePunctuation.setHorizontalAlignment(SwingConstants.CENTER);
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 selected 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 and
344       // colons are allowed in the IDs...
345
346       boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
347       replacePunctuation.setEnabled(enablePunct);
348
349     } catch (Exception ex)
350     {
351       exampleAccession.setText("");
352       replacePunctuation.setEnabled(true);
353     }
354     repaint();
355   }
356
357   /**
358    * Sets the text of the example query to incorporate the example accession
359    * provided by the selected database source
360    * 
361    * @param selectedDatabase
362    * @return
363    */
364   protected String updateExampleQuery(String selectedDatabase)
365   {
366     String eq = getExampleQueries(selectedDatabase);
367     exampleAccession.setText(MessageManager
368             .formatMessage("label.example_query_param", new String[]
369             { eq }));
370     return eq;
371   }
372
373   /**
374    * Action on clicking the 'Example' button is to write the example accession
375    * as the query text field value
376    */
377   protected void example_actionPerformed()
378   {
379     String eq = getExampleQueries((String) database.getSelectedItem());
380     textArea.setText(eq);
381     repaint();
382   }
383
384   /**
385    * Clears the query input field
386    */
387   protected void clear_actionPerformed()
388   {
389     textArea.setText("");
390     repaint();
391   }
392
393   /**
394    * Action on Close button is to close this frame, and also (if it is embedded
395    * in a search panel) to close the search panel
396    * 
397    * @param e
398    */
399   protected void close_actionPerformed(ActionEvent e)
400   {
401     try
402     {
403       frame.setClosed(true);
404       if (parentSearchPanel != null)
405       {
406         parentSearchPanel.btn_cancel_ActionPerformed();
407       }
408     } catch (Exception ex)
409     {
410     }
411   }
412
413   /**
414    * Action on OK is to start the fetch for entered accession(s)
415    */
416   public void ok_actionPerformed()
417   {
418     /*
419      * tidy inputs and check there is something to search for
420      */
421     String text = textArea.getText();
422     if (replacePunctuation.isEnabled() && replacePunctuation.isSelected())
423     {
424       text = text.replace(",", ";");
425     }
426     text = text.replaceAll("(\\s|[,; ])+", ";");
427     textArea.setText(text);
428     if (text.isEmpty())
429     {
430       // todo i18n
431       showErrorMessage(
432               "Please enter a (semi-colon separated list of) database id(s)");
433       resetDialog();
434       return;
435     }
436     exampleBtn.setEnabled(false);
437     textArea.setEnabled(false);
438     okBtn.setEnabled(false);
439     closeBtn.setEnabled(false);
440     backBtn.setEnabled(false);
441
442     Thread worker = new Thread(this);
443     worker.start();
444   }
445
446   private void resetDialog()
447   {
448     exampleBtn.setEnabled(true);
449     textArea.setEnabled(true);
450     okBtn.setEnabled(true);
451     closeBtn.setEnabled(true);
452     backBtn.setEnabled(parentSearchPanel != null);
453   }
454
455   @Override
456   public void run()
457   {
458     boolean addToLast = false;
459     List<String> aresultq = new ArrayList<>();
460     List<String> presultTitle = new ArrayList<>();
461     List<AlignmentI> presult = new ArrayList<>();
462     List<AlignmentI> aresult = new ArrayList<>();
463     List<DbSourceProxy> sources = sfetch
464             .getSourceProxy((String) database.getSelectedItem());
465     Iterator<DbSourceProxy> proxies = sources.iterator();
466     String[] qries;
467     List<String> nextFetch = Arrays
468             .asList(qries = textArea.getText().split(";"));
469     Iterator<String> en = Arrays.asList(new String[0]).iterator();
470     int nqueries = qries.length;
471
472     FeatureSettingsModelI preferredFeatureColours = null;
473     while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
474     {
475       if (!en.hasNext() && nextFetch.size() > 0)
476       {
477         en = nextFetch.iterator();
478         nqueries = nextFetch.size();
479         // save the remaining queries in the original array
480         qries = nextFetch.toArray(new String[nqueries]);
481         nextFetch = new ArrayList<>();
482       }
483
484       DbSourceProxy proxy = proxies.next();
485       try
486       {
487         // update status
488         guiWindow.setProgressBar(MessageManager.formatMessage(
489                 "status.fetching_sequence_queries_from", new String[]
490                 { Integer.valueOf(nqueries).toString(),
491                     proxy.getDbName() }),
492                 Thread.currentThread().hashCode());
493         if (proxy.getMaximumQueryCount() == 1)
494         {
495           /*
496            * proxy only handles one accession id at a time
497            */
498           while (en.hasNext())
499           {
500             String acc = en.next();
501             if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
502             {
503               nextFetch.add(acc);
504             }
505           }
506         }
507         else
508         {
509           /*
510            * proxy can fetch multiple accessions at one time
511            */
512           fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
513         }
514       } catch (Exception e)
515       {
516         showErrorMessage("Error retrieving " + textArea.getText() + " from "
517                 + database.getSelectedItem());
518         // error
519         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
520         System.err.println("Retrieval failed for source ='"
521                 + database.getSelectedItem() + "' and query\n'"
522                 + textArea.getText() + "'\n");
523         e.printStackTrace();
524       } catch (OutOfMemoryError e)
525       {
526         showErrorMessage("Out of Memory when retrieving "
527                 + textArea.getText() + " from " + database.getSelectedItem()
528                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
529         e.printStackTrace();
530       } catch (Error e)
531       {
532         showErrorMessage("Serious Error retrieving " + textArea.getText()
533                 + " from " + database.getSelectedItem());
534         e.printStackTrace();
535       }
536
537       // Stack results ready for opening in alignment windows
538       if (aresult != null && aresult.size() > 0)
539       {
540         FeatureSettingsModelI proxyColourScheme = proxy
541                 .getFeatureColourScheme();
542         if (proxyColourScheme != null)
543         {
544           preferredFeatureColours = proxyColourScheme;
545         }
546
547         AlignmentI ar = null;
548         if (proxy.isAlignmentSource())
549         {
550           addToLast = false;
551           // new window for each result
552           while (aresult.size() > 0)
553           {
554             presult.add(aresult.remove(0));
555             presultTitle.add(
556                     aresultq.remove(0) + " " + getDefaultRetrievalTitle());
557           }
558         }
559         else
560         {
561           String titl = null;
562           if (addToLast && presult.size() > 0)
563           {
564             ar = presult.remove(presult.size() - 1);
565             titl = presultTitle.remove(presultTitle.size() - 1);
566           }
567           // concatenate all results in one window
568           while (aresult.size() > 0)
569           {
570             if (ar == null)
571             {
572               ar = aresult.remove(0);
573             }
574             else
575             {
576               ar.append(aresult.remove(0));
577             }
578           }
579           addToLast = true;
580           presult.add(ar);
581           presultTitle.add(titl);
582         }
583       }
584       guiWindow.setProgressBar(
585               MessageManager.getString("status.finshed_querying"),
586               Thread.currentThread().hashCode());
587     }
588     guiWindow
589             .setProgressBar(
590                     (presult.size() > 0)
591                             ? MessageManager
592                                     .getString("status.parsing_results")
593                             : MessageManager.getString("status.processing"),
594                     Thread.currentThread().hashCode());
595     // process results
596     while (presult.size() > 0)
597     {
598       parseResult(presult.remove(0), presultTitle.remove(0), null,
599               preferredFeatureColours);
600     }
601     // only remove visual delay after we finished parsing.
602     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
603     if (nextFetch.size() > 0)
604     {
605       StringBuffer sb = new StringBuffer();
606       sb.append("Didn't retrieve the following "
607               + (nextFetch.size() == 1 ? "query"
608                       : nextFetch.size() + " queries")
609               + ": \n");
610       int l = sb.length(), lr = 0;
611       for (String s : nextFetch)
612       {
613         if (l != sb.length())
614         {
615           sb.append("; ");
616         }
617         if (lr - sb.length() > 40)
618         {
619           sb.append("\n");
620         }
621         sb.append(s);
622       }
623       showErrorMessage(sb.toString());
624     }
625     resetDialog();
626   }
627
628   /**
629    * Tries to fetch one or more accession ids from the database proxy
630    * 
631    * @param proxy
632    * @param accessions
633    *          the queries to fetch
634    * @param aresultq
635    *          a successful queries list to add to
636    * @param aresult
637    *          a list of retrieved alignments to add to
638    * @param nextFetch
639    *          failed queries are added to this list
640    * @throws Exception
641    */
642   void fetchMultipleAccessions(DbSourceProxy proxy,
643           Iterator<String> accessions, List<String> aresultq,
644           List<AlignmentI> aresult, List<String> nextFetch) throws Exception
645   {
646     StringBuilder multiacc = new StringBuilder();
647     List<String> tosend = new ArrayList<>();
648     while (accessions.hasNext())
649     {
650       String nel = accessions.next();
651       tosend.add(nel);
652       multiacc.append(nel);
653       if (accessions.hasNext())
654       {
655         multiacc.append(proxy.getAccessionSeparator());
656       }
657     }
658
659     try
660     {
661       String query = multiacc.toString();
662       AlignmentI rslt = proxy.getSequenceRecords(query);
663       if (rslt == null || rslt.getHeight() == 0)
664       {
665         // no results - pass on all queries to next source
666         nextFetch.addAll(tosend);
667       }
668       else
669       {
670         aresultq.add(query);
671         aresult.add(rslt);
672         if (tosend.size() > 1)
673         {
674           checkResultForQueries(rslt, tosend, nextFetch, proxy);
675         }
676       }
677     } catch (OutOfMemoryError oome)
678     {
679       new OOMWarning("fetching " + multiacc + " from "
680               + database.getSelectedItem(), oome, this);
681     }
682   }
683
684   /**
685    * Query for a single accession id via the database proxy
686    * 
687    * @param proxy
688    * @param accession
689    * @param aresultq
690    *          a list of successful queries to add to
691    * @param aresult
692    *          a list of retrieved alignments to add to
693    * @return true if the fetch was successful, else false
694    */
695   boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
696           List<String> aresultq, List<AlignmentI> aresult)
697   {
698     boolean success = false;
699     try
700     {
701       if (aresult != null)
702       {
703         try
704         {
705           // give the server a chance to breathe
706           Thread.sleep(5);
707         } catch (Exception e)
708         {
709           //
710         }
711       }
712
713       AlignmentI indres = null;
714       try
715       {
716         indres = proxy.getSequenceRecords(accession);
717       } catch (OutOfMemoryError oome)
718       {
719         new OOMWarning(
720                 "fetching " + accession + " from " + proxy.getDbName(),
721                 oome, this);
722       }
723       if (indres != null)
724       {
725         aresultq.add(accession);
726         aresult.add(indres);
727         success = true;
728       }
729     } catch (Exception e)
730     {
731       Cache.log.info("Error retrieving " + accession + " from "
732               + proxy.getDbName(), e);
733     }
734     return success;
735   }
736
737   /**
738    * Checks which of the queries were successfully retrieved by searching the
739    * DBRefs of the retrieved sequences for a match. Any not found are added to
740    * the 'nextFetch' list.
741    * 
742    * @param rslt
743    * @param queries
744    * @param nextFetch
745    * @param proxy
746    */
747   void checkResultForQueries(AlignmentI rslt, List<String> queries,
748           List<String> nextFetch, DbSourceProxy proxy)
749   {
750     SequenceI[] rs = rslt.getSequencesArray();
751
752     for (String q : queries)
753     {
754       DBRefEntry dbr = new DBRefEntry();
755       dbr.setSource(proxy.getDbSource());
756       dbr.setVersion(null);
757       String accId = proxy.getAccessionIdFromQuery(q);
758       dbr.setAccessionId(accId);
759       boolean rfound = false;
760       for (int r = 0; r < rs.length; r++)
761       {
762         if (rs[r] != null)
763         {
764           List<DBRefEntry> found = DBRefUtils.searchRefs(rs[r].getDBRefs(),
765                   accId);
766           if (!found.isEmpty())
767           {
768             rfound = true;
769             break;
770           }
771         }
772       }
773       if (!rfound)
774       {
775         nextFetch.add(q);
776       }
777     }
778   }
779
780   /**
781    * 
782    * @return a standard title for any results retrieved using the currently
783    *         selected source and settings
784    */
785   public String getDefaultRetrievalTitle()
786   {
787     return "Retrieved from " + database.getSelectedItem();
788   }
789
790   AlignmentI parseResult(AlignmentI al, String title,
791           FileFormatI currentFileFormat,
792           FeatureSettingsModelI preferredFeatureColours)
793   {
794
795     if (al != null && al.getHeight() > 0)
796     {
797       if (title == null)
798       {
799         title = getDefaultRetrievalTitle();
800       }
801       if (alignFrame == null)
802       {
803         AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
804                 AlignFrame.DEFAULT_HEIGHT);
805         if (currentFileFormat != null)
806         {
807           af.currentFileFormat = currentFileFormat;
808         }
809
810         List<SequenceI> alsqs = al.getSequences();
811         synchronized (alsqs)
812         {
813           for (SequenceI sq : alsqs)
814           {
815             if (sq.getFeatures().hasFeatures())
816             {
817               af.setShowSeqFeatures(true);
818               break;
819             }
820           }
821         }
822
823         if (preferredFeatureColours != null)
824         {
825           af.getViewport().applyFeaturesStyle(preferredFeatureColours);
826         }
827         if (Cache.getDefault("HIDE_INTRONS", true))
828         {
829           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
830         }
831         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
832                 AlignFrame.DEFAULT_HEIGHT);
833
834         af.setStatus(MessageManager
835                 .getString("label.successfully_pasted_alignment_file"));
836
837         try
838         {
839           af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
840         } catch (Exception ex)
841         {
842         }
843       }
844       else
845       {
846         alignFrame.viewport.addAlignment(al, title);
847       }
848     }
849     return al;
850   }
851
852   void showErrorMessage(final String error)
853   {
854     resetDialog();
855     javax.swing.SwingUtilities.invokeLater(new Runnable()
856     {
857       @Override
858       public void run()
859       {
860         JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
861                 MessageManager.getString("label.error_retrieving_data"),
862                 JvOptionPane.WARNING_MESSAGE);
863       }
864     });
865   }
866
867   public IProgressIndicator getProgressIndicator()
868   {
869     return progressIndicator;
870   }
871
872   public void setProgressIndicator(IProgressIndicator progressIndicator)
873   {
874     this.progressIndicator = progressIndicator;
875   }
876
877   /**
878    * Hide this panel (on clicking the database button to open the database
879    * chooser)
880    */
881   void hidePanel()
882   {
883     frame.setVisible(false);
884   }
885
886   public void setQuery(String ids)
887   {
888     textArea.setText(ids);
889   }
890
891   /**
892    * Called to modify the search panel for embedding as an alternative tab of a
893    * free text search panel. The database choice list is hidden (since the
894    * choice has been made), and a Back button is made visible (which reopens the
895    * Sequence Fetcher panel).
896    * 
897    * @param parentPanel
898    */
899   public void embedIn(GFTSPanel parentPanel)
900   {
901     database.setVisible(false);
902     backBtn.setVisible(true);
903     parentSearchPanel = parentPanel;
904   }
905 }