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