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