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