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