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