JAL-3304 option to export linked features in Jalview format
[jalview.git] / src / jalview / gui / AnnotationExporter.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.FeatureRenderer;
24 import jalview.bin.Cache;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.SequenceI;
27 import jalview.io.AnnotationFile;
28 import jalview.io.FeaturesFile;
29 import jalview.io.JalviewFileChooser;
30 import jalview.io.JalviewFileView;
31 import jalview.util.MessageManager;
32
33 import java.awt.Color;
34 import java.awt.Dimension;
35 import java.awt.event.ActionEvent;
36 import java.awt.event.ActionListener;
37 import java.io.FileWriter;
38 import java.io.PrintWriter;
39
40 import javax.swing.BoxLayout;
41 import javax.swing.ButtonGroup;
42 import javax.swing.JButton;
43 import javax.swing.JCheckBox;
44 import javax.swing.JInternalFrame;
45 import javax.swing.JLabel;
46 import javax.swing.JLayeredPane;
47 import javax.swing.JPanel;
48 import javax.swing.JRadioButton;
49 import javax.swing.SwingConstants;
50
51 /**
52  * 
53  * GUI dialog for exporting features or alignment annotations depending upon
54  * which method is called.
55  * 
56  * @author AMW
57  * 
58  */
59 public class AnnotationExporter extends JPanel
60 {
61   private JInternalFrame frame;
62
63   private AlignmentPanel ap;
64
65   /*
66    * true if exporting features, false if exporting annotations
67    */
68   private boolean exportFeatures = true;
69
70   private AlignmentAnnotation[] annotations;
71
72   private boolean wholeView;
73
74   /*
75    * option to export linked (CDS/peptide) features when shown 
76    * on the alignment, converted to this alignment's coordinates
77    */
78   private JCheckBox includeLinkedFeatures;
79
80   /*
81    * output format option shown for feature export
82    */
83   JRadioButton GFFFormat = new JRadioButton();
84
85   /*
86    * output format option shown for annotation export
87    */
88   JRadioButton CSVFormat = new JRadioButton();
89
90   private JPanel linkedFeaturesPanel;
91
92   /**
93    * Constructor
94    * 
95    * @param panel
96    */
97   public AnnotationExporter(AlignmentPanel panel)
98   {
99     this.ap = panel;
100     try
101     {
102       jbInit();
103     } catch (Exception ex)
104     {
105       ex.printStackTrace();
106     }
107
108     frame = new JInternalFrame();
109     frame.setContentPane(this);
110     frame.setLayer(JLayeredPane.PALETTE_LAYER);
111     Dimension preferredSize = frame.getPreferredSize();
112     Desktop.addInternalFrame(frame, "", true, preferredSize.width,
113             preferredSize.height, true, true);
114   }
115
116   /**
117    * Configures the dialog for options to export visible features. If from a split
118    * frame panel showing linked features, make the option to include these in the
119    * export visible.
120    */
121   public void exportFeatures()
122   {
123     exportFeatures = true;
124     CSVFormat.setVisible(false);
125     if (ap.av.isShowComplementFeatures())
126     {
127       linkedFeaturesPanel.setVisible(true);
128       frame.pack();
129     }
130     frame.setTitle(MessageManager.getString("label.export_features"));
131   }
132
133   /**
134    * Configures the dialog for options to export all visible annotations
135    */
136   public void exportAnnotations()
137   {
138     boolean showAnnotation = ap.av.isShowAnnotation();
139     exportAnnotation(showAnnotation ? null
140             : ap.av.getAlignment().getAlignmentAnnotation(), true);
141   }
142
143   /**
144    * Configures the dialog for options to export the given annotation row
145    * 
146    * @param toExport
147    */
148   public void exportAnnotation(AlignmentAnnotation toExport)
149   {
150     exportAnnotation(new AlignmentAnnotation[] { toExport }, false);
151   }
152
153   private void exportAnnotation(AlignmentAnnotation[] toExport,
154           boolean forWholeView)
155   {
156     wholeView = forWholeView;
157     annotations = toExport;
158     exportFeatures = false;
159     GFFFormat.setVisible(false);
160     CSVFormat.setVisible(true);
161     frame.setTitle(MessageManager.getString("label.export_annotations"));
162   }
163
164   private void toFile_actionPerformed()
165   {
166     JalviewFileChooser chooser = new JalviewFileChooser(
167             Cache.getProperty("LAST_DIRECTORY"));
168
169     chooser.setFileView(new JalviewFileView());
170     chooser.setDialogTitle(exportFeatures
171             ? MessageManager.getString("label.save_features_to_file")
172             : MessageManager.getString("label.save_annotation_to_file"));
173     chooser.setToolTipText(MessageManager.getString("action.save"));
174
175     int value = chooser.showSaveDialog(this);
176
177     if (value == JalviewFileChooser.APPROVE_OPTION)
178     {
179       String text = getText();
180
181       try
182       {
183         PrintWriter out = new PrintWriter(
184                 new FileWriter(chooser.getSelectedFile()));
185         out.print(text);
186         out.close();
187       } catch (Exception ex)
188       {
189         ex.printStackTrace();
190       }
191     }
192
193     close_actionPerformed();
194   }
195
196   /**
197    * Answers the text to output for either Features (in GFF or Jalview format) or
198    * Annotations (in CSV or Jalview format)
199    * 
200    * @return
201    */
202   private String getText()
203   {
204     return exportFeatures ? getFeaturesText() : getAnnotationsText();
205   }
206
207   /**
208    * Returns the text contents for output of annotations in either CSV or Jalview
209    * format
210    * 
211    * @return
212    */
213   private String getAnnotationsText()
214   {
215     String text;
216     if (CSVFormat.isSelected())
217     {
218       text = new AnnotationFile().printCSVAnnotations(annotations);
219     }
220     else
221     {
222       if (wholeView)
223       {
224         text = new AnnotationFile().printAnnotationsForView(ap.av);
225       }
226       else
227       {
228         text = new AnnotationFile().printAnnotations(annotations, null,
229                 null);
230       }
231     }
232     return text;
233   }
234
235   /**
236    * Returns the text contents for output of features in either GFF or Jalview
237    * format
238    * 
239    * @return
240    */
241   private String getFeaturesText()
242   {
243     String text;
244     SequenceI[] sequences = ap.av.getAlignment().getSequencesArray();
245     boolean includeNonPositional = ap.av.isShowNPFeats();
246
247     FeaturesFile formatter = new FeaturesFile();
248     final FeatureRenderer fr = ap.getFeatureRenderer();
249     boolean includeComplement = includeLinkedFeatures.isSelected();
250
251     if (GFFFormat.isSelected())
252     {
253       text = formatter.printGffFormat(sequences, fr, includeNonPositional,
254               includeComplement);
255     }
256     else
257     {
258       text = formatter.printJalviewFormat(sequences, fr,
259               includeNonPositional, includeComplement);
260     }
261     return text;
262   }
263
264   private void toTextbox_actionPerformed()
265   {
266     CutAndPasteTransfer cap = new CutAndPasteTransfer();
267
268     try
269     {
270       String text = getText();
271       cap.setText(text);
272       Desktop.addInternalFrame(cap, (exportFeatures ? MessageManager
273               .formatMessage("label.features_for_params", new String[]
274               { ap.alignFrame.getTitle() })
275               : MessageManager.formatMessage("label.annotations_for_params",
276                       new String[]
277                       { ap.alignFrame.getTitle() })),
278               600, 500);
279     } catch (OutOfMemoryError oom)
280     {
281       new OOMWarning((exportFeatures ? MessageManager.formatMessage(
282               "label.generating_features_for_params", new String[]
283               { ap.alignFrame.getTitle() })
284               : MessageManager.formatMessage(
285                       "label.generating_annotations_for_params",
286                       new String[]
287                       { ap.alignFrame.getTitle() })),
288               oom);
289       cap.dispose();
290     }
291
292     close_actionPerformed();
293   }
294
295   private void close_actionPerformed()
296   {
297     try
298     {
299       frame.setClosed(true);
300     } catch (java.beans.PropertyVetoException ex)
301     {
302     }
303   }
304
305   /**
306    * Adds widgets to the panel
307    * 
308    * @throws Exception
309    */
310   private void jbInit() throws Exception
311   {
312     this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
313     this.setBackground(Color.white);
314
315     JPanel formatPanel = buildFormatOptionsPanel();
316     JPanel linkedFeatures = buildLinkedFeaturesPanel();
317     JPanel actionsPanel = buildActionsPanel();
318
319     this.add(formatPanel);
320     this.add(linkedFeatures);
321     this.add(actionsPanel);
322   }
323
324   /**
325    * Builds a panel with a checkbox for the option to export linked (CDS/peptide)
326    * features. This is hidden by default, and only made visible if exporting
327    * features from a split frame panel which is configured to show linked
328    * features.
329    * 
330    * @return
331    */
332   private JPanel buildLinkedFeaturesPanel()
333   {
334     linkedFeaturesPanel = new JPanel();
335     linkedFeaturesPanel.setOpaque(false);
336
337     boolean nucleotide = ap.av.isNucleotide();
338     String complement = nucleotide
339             ? MessageManager.getString("label.protein").toLowerCase()
340             : "CDS";
341     JLabel label = new JLabel(
342             MessageManager.formatMessage("label.include_linked_features",
343                     complement));
344     label.setHorizontalAlignment(SwingConstants.TRAILING);
345     String tooltip = MessageManager
346             .formatMessage("label.include_linked_tooltip", complement);
347     label.setToolTipText(
348             JvSwingUtils.wrapTooltip(true, tooltip));
349
350     includeLinkedFeatures = new JCheckBox();
351     linkedFeaturesPanel.add(label);
352     linkedFeaturesPanel.add(includeLinkedFeatures);
353     linkedFeaturesPanel.setVisible(false);
354
355     return linkedFeaturesPanel;
356   }
357
358   /**
359    * Builds the panel with to File or Textbox or Close actions
360    * 
361    * @return
362    */
363   JPanel buildActionsPanel()
364   {
365     JPanel actionsPanel = new JPanel();
366     actionsPanel.setOpaque(false);
367
368     JButton toFile = new JButton(MessageManager.getString("label.to_file"));
369     toFile.addActionListener(new ActionListener()
370     {
371       @Override
372       public void actionPerformed(ActionEvent e)
373       {
374         toFile_actionPerformed();
375       }
376     });
377     JButton toTextbox = new JButton(
378             MessageManager.getString("label.to_textbox"));
379     toTextbox.addActionListener(new ActionListener()
380     {
381       @Override
382       public void actionPerformed(ActionEvent e)
383       {
384         toTextbox_actionPerformed();
385       }
386     });
387     JButton close = new JButton(MessageManager.getString("action.close"));
388     close.addActionListener(new ActionListener()
389     {
390       @Override
391       public void actionPerformed(ActionEvent e)
392       {
393         close_actionPerformed();
394       }
395     });
396
397     actionsPanel.add(toFile);
398     actionsPanel.add(toTextbox);
399     actionsPanel.add(close);
400
401     return actionsPanel;
402   }
403
404   /**
405    * Builds the panel with options to output in Jalview, GFF or CSV format. GFF is
406    * only made visible when exporting features, CSV only when exporting
407    * annotation.
408    * 
409    * @return
410    */
411   JPanel buildFormatOptionsPanel()
412   {
413     JPanel formatPanel = new JPanel();
414     // formatPanel.setBorder(BorderFactory.createEtchedBorder());
415     formatPanel.setOpaque(false);
416
417     JRadioButton jalviewFormat = new JRadioButton("Jalview");
418     jalviewFormat.setOpaque(false);
419     jalviewFormat.setSelected(true);
420     GFFFormat.setOpaque(false);
421     GFFFormat.setText("GFF");
422     CSVFormat.setOpaque(false);
423     CSVFormat.setText(MessageManager.getString("label.csv_spreadsheet"));
424
425     ButtonGroup buttonGroup = new ButtonGroup();
426     buttonGroup.add(jalviewFormat);
427     buttonGroup.add(GFFFormat);
428     buttonGroup.add(CSVFormat);
429
430     JLabel format = new JLabel(
431             MessageManager.getString("action.format") + " ");
432     format.setHorizontalAlignment(SwingConstants.TRAILING);
433
434     formatPanel.add(format);
435     formatPanel.add(jalviewFormat);
436     formatPanel.add(GFFFormat);
437     formatPanel.add(CSVFormat);
438
439     return formatPanel;
440   }
441 }