c65873481aa4ab96804f3a08e5edb0a59c74dc43
[jalview.git] / CutAndPasteTransfer.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.appletgui;
22
23 import jalview.analysis.AlignmentUtils;
24 import jalview.api.ComplexAlignFile;
25 import jalview.api.FeaturesSourceI;
26 import jalview.bin.JalviewLite;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.ColumnSelection;
29 import jalview.datamodel.PDBEntry;
30 import jalview.datamodel.SequenceI;
31 import jalview.io.AlignmentFileReaderI;
32 import jalview.io.AnnotationFile;
33 import jalview.io.AppletFormatAdapter;
34 import jalview.io.DataSourceType;
35 import jalview.io.FileFormatI;
36 import jalview.io.IdentifyFile;
37 import jalview.io.NewickFile;
38 import jalview.io.TCoffeeScoreFile;
39 import jalview.json.binding.biojson.v1.ColourSchemeMapper;
40 import jalview.schemes.ColourSchemeI;
41 import jalview.schemes.TCoffeeColourScheme;
42 import jalview.util.MessageManager;
43
44 import java.awt.BorderLayout;
45 import java.awt.Button;
46 import java.awt.Dialog;
47 import java.awt.Font;
48 import java.awt.Frame;
49 import java.awt.Label;
50 import java.awt.Panel;
51 import java.awt.TextArea;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ActionListener;
54 import java.awt.event.MouseEvent;
55 import java.awt.event.MouseListener;
56 import java.io.IOException;
57
58 public class CutAndPasteTransfer extends Panel implements ActionListener,
59         MouseListener
60 {
61   boolean pdbImport = false;
62
63   boolean treeImport = false;
64
65   boolean annotationImport = false;
66
67   SequenceI seq;
68
69   AlignFrame alignFrame;
70
71   AlignmentFileReaderI source = null;
72
73   public CutAndPasteTransfer(boolean forImport, AlignFrame alignFrame)
74   {
75     try
76     {
77       jbInit();
78     } catch (Exception e)
79     {
80       e.printStackTrace();
81     }
82
83     this.alignFrame = alignFrame;
84
85     if (!forImport)
86     {
87       buttonPanel.setVisible(false);
88     }
89   }
90
91   public String getText()
92   {
93     return textarea.getText();
94   }
95
96   public void setText(String text)
97   {
98     textarea.setText(text);
99   }
100
101   public void setPDBImport(SequenceI seq)
102   {
103     this.seq = seq;
104     accept.setLabel(MessageManager.getString("action.accept"));
105     addSequences.setVisible(false);
106     pdbImport = true;
107   }
108
109   public void setTreeImport()
110   {
111     treeImport = true;
112     accept.setLabel(MessageManager.getString("action.accept"));
113     addSequences.setVisible(false);
114   }
115
116   public void setAnnotationImport()
117   {
118     annotationImport = true;
119     accept.setLabel(MessageManager.getString("action.accept"));
120     addSequences.setVisible(false);
121   }
122
123   @Override
124   public void actionPerformed(ActionEvent evt)
125   {
126     if (evt.getSource() == accept)
127     {
128       ok(true);
129     }
130     else if (evt.getSource() == addSequences)
131     {
132       ok(false);
133     }
134     else if (evt.getSource() == cancel)
135     {
136       cancel();
137     }
138   }
139
140   protected void ok(boolean newWindow)
141   {
142     String text = getText();
143     int length = text.length();
144     textarea.append("\n");
145     if (textarea.getText().length() == length)
146     {
147       String warning = "\n\n#################################################\n"
148               + "WARNING!! THIS IS THE MAXIMUM SIZE OF TEXTAREA!!\n"
149               + "\nCAN'T INPUT FULL ALIGNMENT"
150               + "\n\nYOU MUST DELETE THIS WARNING TO CONTINUE"
151               + "\n\nMAKE SURE LAST SEQUENCE PASTED IS COMPLETE"
152               + "\n#################################################\n";
153       textarea.setText(text.substring(0, text.length() - warning.length())
154               + warning);
155
156       textarea.setCaretPosition(text.length());
157     }
158
159     if (pdbImport)
160     {
161       openPdbViewer(text);
162
163     }
164     else if (treeImport)
165     {
166       if (!loadTree())
167       {
168         return;
169       }
170     }
171     else if (annotationImport)
172     {
173       loadAnnotations();
174     }
175     else if (alignFrame != null)
176     {
177       loadAlignment(text, newWindow, alignFrame.getAlignViewport());
178     }
179
180     // TODO: dialog should indicate if data was parsed correctly or not - see
181     // JAL-1102
182     if (this.getParent() instanceof Frame)
183     {
184       ((Frame) this.getParent()).setVisible(false);
185     }
186     else
187     {
188       ((Dialog) this.getParent()).setVisible(false);
189     }
190   }
191
192   /**
193    * Parses text as Newick Tree format, and loads on to the alignment. Returns
194    * true if successful, else false.
195    */
196   protected boolean loadTree()
197   {
198     try
199     {
200       NewickFile fin = new NewickFile(textarea.getText(),
201               DataSourceType.PASTE);
202
203       fin.parse();
204       if (fin.getTree() != null)
205       {
206         alignFrame.loadTree(fin, "Pasted tree file");
207         return true;
208       }
209     } catch (Exception ex)
210     {
211       // TODO: JAL-1102 - should have a warning message in dialog, not simply
212       // overwrite the broken input data with the exception
213       textarea.setText(MessageManager.formatMessage(
214               "label.could_not_parse_newick_file",
215               new Object[] { ex.getMessage() }));
216       return false;
217     }
218     return false;
219   }
220
221   /**
222    * Parse text as an alignment file and add to the current or a new window.
223    * 
224    * @param text
225    * @param newWindow
226    */
227   protected void loadAlignment(String text, boolean newWindow,
228           AlignViewport viewport)
229   {
230     AlignmentI al = null;
231
232     try
233     {
234       FileFormatI format = new IdentifyFile().identify(text,
235               DataSourceType.PASTE);
236       AppletFormatAdapter afa = new AppletFormatAdapter(
237               alignFrame.alignPanel);
238       al = afa.readFile(text, DataSourceType.PASTE, format);
239       source = afa.getAlignFile();
240
241     if (al != null)
242     {
243       al.setDataset(null); // set dataset on alignment/sequences
244
245       /*
246        * SplitFrame option dependent on applet parameter for now.
247        */
248       boolean allowSplitFrame = alignFrame.viewport.applet
249               .getDefaultParameter("enableSplitFrame", false);
250       if (allowSplitFrame && openSplitFrame(al, format))
251       {
252         return;
253       }
254       if (newWindow)
255       {
256         AlignFrame af;
257
258         if (source instanceof ComplexAlignFile)
259         {
260           ColumnSelection colSel = ((ComplexAlignFile) source)
261                   .getColumnSelection();
262           SequenceI[] hiddenSeqs = ((ComplexAlignFile) source)
263                   .getHiddenSequences();
264           boolean showSeqFeatures = ((ComplexAlignFile) source)
265                   .isShowSeqFeatures();
266           String colourSchemeName = ((ComplexAlignFile) source)
267                   .getGlobalColourScheme();
268           af = new AlignFrame(al, hiddenSeqs, colSel,
269                   alignFrame.viewport.applet, "Cut & Paste input - "
270                           + format, false);
271           af.getAlignViewport().setShowSequenceFeatures(showSeqFeatures);
272           ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
273                   colourSchemeName, al);
274           if (cs != null)
275           {
276             af.changeColour(cs);
277           }
278         }
279         else
280         {
281           af = new AlignFrame(al, alignFrame.viewport.applet,
282                   "Cut & Paste input - " + format, false);
283           if (source instanceof FeaturesSourceI)
284           {
285             af.getAlignViewport().setShowSequenceFeatures(true);
286           }
287         }
288
289         af.statusBar
290                 .setText(MessageManager
291                         .getString("label.successfully_pasted_annotation_to_alignment"));
292       }
293       else
294       {
295         alignFrame.addSequences(al.getSequencesArray());
296         alignFrame.statusBar.setText(MessageManager
297                 .getString("label.successfully_pasted_alignment_file"));
298       }
299     }
300     } catch (IOException ex)
301     {
302       ex.printStackTrace();
303     }
304   }
305
306   /**
307    * Check whether the new alignment could be mapped to the current one as
308    * cDNA/protein, if so offer the option to open as split frame view. Returns
309    * true if a split frame view is opened, false if not.
310    * 
311    * @param al
312    * @return
313    */
314   protected boolean openSplitFrame(AlignmentI al, FileFormatI format)
315   {
316     final AlignmentI thisAlignment = this.alignFrame.getAlignViewport()
317             .getAlignment();
318     if (thisAlignment.isNucleotide() == al.isNucleotide())
319     {
320       // both nucleotide or both protein
321       return false;
322     }
323     AlignmentI protein = thisAlignment.isNucleotide() ? al : thisAlignment;
324     AlignmentI dna = thisAlignment.isNucleotide() ? thisAlignment : al;
325     boolean mapped = AlignmentUtils.mapProteinAlignmentToCdna(protein, dna);
326     if (!mapped)
327     {
328       return false;
329     }
330
331     /*
332      * A mapping is possible; ask user if they want a split frame.
333      */
334     String title = MessageManager.getString("label.open_split_window");
335     final JVDialog dialog = new JVDialog((Frame) this.getParent(), title,
336             true, 100, 400);
337     dialog.ok.setLabel(MessageManager.getString("action.yes"));
338     dialog.cancel.setLabel(MessageManager.getString("action.no"));
339     Panel question = new Panel(new BorderLayout());
340     final String text = MessageManager
341             .getString("label.open_split_window?");
342     question.add(new Label(text, Label.CENTER), BorderLayout.CENTER);
343     dialog.setMainPanel(question);
344     dialog.setVisible(true);
345     dialog.toFront();
346
347     if (!dialog.accept)
348     {
349       return false;
350     }
351
352     /*
353      * 'align' the added alignment to match the current one
354      */
355     al.alignAs(thisAlignment);
356
357     /*
358      * Open SplitFrame with DNA above and protein below, including the alignment
359      * from textbox and a copy of the original.
360      */
361     final JalviewLite applet = this.alignFrame.viewport.applet;
362     AlignFrame copyFrame = new AlignFrame(
363             this.alignFrame.viewport.getAlignment(), applet,
364             alignFrame.getTitle(), false, false);
365     AlignFrame newFrame = new AlignFrame(al, alignFrame.viewport.applet,
366             "Cut & Paste input - " + format, false, false);
367     AlignFrame dnaFrame = al.isNucleotide() ? newFrame : copyFrame;
368     AlignFrame proteinFrame = al.isNucleotide() ? copyFrame : newFrame;
369     SplitFrame sf = new SplitFrame(dnaFrame, proteinFrame);
370     sf.addToDisplay(false, applet);
371     return true;
372   }
373
374   /**
375    * Parse the text as a TCoffee score file, if successful add scores as
376    * alignment annotations.
377    */
378   protected void loadAnnotations()
379   {
380     TCoffeeScoreFile tcf = null;
381     try
382     {
383       tcf = new TCoffeeScoreFile(textarea.getText(),
384               jalview.io.DataSourceType.PASTE);
385       if (tcf.isValid())
386       {
387         if (tcf.annotateAlignment(alignFrame.viewport.getAlignment(), true))
388         {
389           alignFrame.tcoffeeColour.setEnabled(true);
390           alignFrame.alignPanel.fontChanged();
391           alignFrame.changeColour(new TCoffeeColourScheme(
392                   alignFrame.viewport.getAlignment()));
393           alignFrame.statusBar
394                   .setText(MessageManager
395                           .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
396         }
397         else
398         {
399           // file valid but didn't get added to alignment for some reason
400           alignFrame.statusBar.setText(MessageManager.formatMessage(
401                   "label.failed_add_tcoffee_scores",
402                   new Object[] { (tcf.getWarningMessage() != null ? tcf
403                           .getWarningMessage() : "") }));
404         }
405       }
406       else
407       {
408         tcf = null;
409       }
410     } catch (Exception x)
411     {
412       tcf = null;
413     }
414     if (tcf == null)
415     {
416       if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
417               textarea.getText(), jalview.io.DataSourceType.PASTE))
418       {
419         alignFrame.alignPanel.fontChanged();
420         alignFrame.alignPanel.setScrollValues(0, 0);
421         alignFrame.statusBar
422                 .setText(MessageManager
423                         .getString("label.successfully_pasted_annotation_to_alignment"));
424
425       }
426       else
427       {
428         if (!alignFrame.parseFeaturesFile(textarea.getText(),
429                 jalview.io.DataSourceType.PASTE))
430         {
431           alignFrame.statusBar
432                   .setText(MessageManager
433                           .getString("label.couldnt_parse_pasted_text_as_valid_annotation_feature_GFF_tcoffee_file"));
434         }
435       }
436     }
437   }
438
439   /**
440    * Open a Jmol viewer (if available), failing that the built-in PDB viewer,
441    * passing the input text as the PDB file data.
442    * 
443    * @param text
444    */
445   protected void openPdbViewer(String text)
446   {
447     PDBEntry pdb = new PDBEntry();
448     pdb.setFile(text);
449
450     if (alignFrame.alignPanel.av.applet.jmolAvailable)
451     {
452       new jalview.appletgui.AppletJmol(pdb, new SequenceI[] { seq }, null,
453               alignFrame.alignPanel, DataSourceType.PASTE);
454     }
455     else
456     {
457       new MCview.AppletPDBViewer(pdb, new SequenceI[] { seq }, null,
458               alignFrame.alignPanel, DataSourceType.PASTE);
459     }
460   }
461
462   protected void cancel()
463   {
464     textarea.setText("");
465     if (this.getParent() instanceof Frame)
466     {
467       ((Frame) this.getParent()).setVisible(false);
468     }
469     else
470     {
471       ((Dialog) this.getParent()).setVisible(false);
472     }
473   }
474
475   protected TextArea textarea = new TextArea();
476
477   Button accept = new Button("New Window");
478
479   Button addSequences = new Button("Add to Current Alignment");
480
481   Button cancel = new Button("Close");
482
483   protected Panel buttonPanel = new Panel();
484
485   BorderLayout borderLayout1 = new BorderLayout();
486
487   private void jbInit() throws Exception
488   {
489     textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 10));
490     textarea.setText(MessageManager
491             .getString("label.paste_your_alignment_file"));
492     textarea.addMouseListener(this);
493     this.setLayout(borderLayout1);
494     accept.addActionListener(this);
495     addSequences.addActionListener(this);
496     cancel.addActionListener(this);
497     this.add(buttonPanel, BorderLayout.SOUTH);
498     buttonPanel.add(accept, null);
499     buttonPanel.add(addSequences);
500     buttonPanel.add(cancel, null);
501     this.add(textarea, java.awt.BorderLayout.CENTER);
502   }
503
504   @Override
505   public void mousePressed(MouseEvent evt)
506   {
507     if (textarea.getText().startsWith(
508             MessageManager.getString("label.paste_your")))
509     {
510       textarea.setText("");
511     }
512   }
513
514   @Override
515   public void mouseReleased(MouseEvent evt)
516   {
517   }
518
519   @Override
520   public void mouseClicked(MouseEvent evt)
521   {
522   }
523
524   @Override
525   public void mouseEntered(MouseEvent evt)
526   {
527   }
528
529   @Override
530   public void mouseExited(MouseEvent evt)
531   {
532   }
533 }