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