JAL-3829 JAL-3865 StructureChooserQuerySource implementation now dependent on the...
[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     // TODO: JAL-3048 JalviewFileChooser - Save option
167     JalviewFileChooser chooser = new JalviewFileChooser(
168             Cache.getProperty("LAST_DIRECTORY"));
169
170     chooser.setFileView(new JalviewFileView());
171     chooser.setDialogTitle(exportFeatures
172             ? MessageManager.getString("label.save_features_to_file")
173             : MessageManager.getString("label.save_annotation_to_file"));
174     chooser.setToolTipText(MessageManager.getString("action.save"));
175
176     int value = chooser.showSaveDialog(this);
177
178     if (value == JalviewFileChooser.APPROVE_OPTION)
179     {
180       String text = getText();
181
182       try
183       {
184         PrintWriter out = new PrintWriter(
185                 new FileWriter(chooser.getSelectedFile()));
186         out.print(text);
187         out.close();
188       } catch (Exception ex)
189       {
190         ex.printStackTrace();
191       }
192     }
193
194     close_actionPerformed();
195   }
196
197   /**
198    * Answers the text to output for either Features (in GFF or Jalview format) or
199    * Annotations (in CSV or Jalview format)
200    * 
201    * @return
202    */
203   private String getText()
204   {
205     return exportFeatures ? getFeaturesText() : getAnnotationsText();
206   }
207
208   /**
209    * Returns the text contents for output of annotations in either CSV or Jalview
210    * format
211    * 
212    * @return
213    */
214   private String getAnnotationsText()
215   {
216     String text;
217     if (CSVFormat.isSelected())
218     {
219       text = new AnnotationFile().printCSVAnnotations(annotations);
220     }
221     else
222     {
223       if (wholeView)
224       {
225         text = new AnnotationFile().printAnnotationsForView(ap.av);
226       }
227       else
228       {
229         text = new AnnotationFile().printAnnotations(annotations, null,
230                 null);
231       }
232     }
233     return text;
234   }
235
236   /**
237    * Returns the text contents for output of features in either GFF or Jalview
238    * format
239    * 
240    * @return
241    */
242   private String getFeaturesText()
243   {
244     String text;
245     SequenceI[] sequences = ap.av.getAlignment().getSequencesArray();
246     boolean includeNonPositional = ap.av.isShowNPFeats();
247
248     FeaturesFile formatter = new FeaturesFile();
249     final FeatureRenderer fr = ap.getFeatureRenderer();
250     boolean includeComplement = includeLinkedFeatures.isSelected();
251
252     if (GFFFormat.isSelected())
253     {
254       text = formatter.printGffFormat(sequences, fr, includeNonPositional,
255               includeComplement);
256     }
257     else
258     {
259       text = formatter.printJalviewFormat(sequences, fr,
260               includeNonPositional, includeComplement);
261     }
262     return text;
263   }
264
265   private void toTextbox_actionPerformed()
266   {
267     CutAndPasteTransfer cap = new CutAndPasteTransfer();
268
269     try
270     {
271       String text = getText();
272       cap.setText(text);
273       Desktop.addInternalFrame(cap, (exportFeatures ? MessageManager
274               .formatMessage("label.features_for_params", new String[]
275               { ap.alignFrame.getTitle() })
276               : MessageManager.formatMessage("label.annotations_for_params",
277                       new String[]
278                       { ap.alignFrame.getTitle() })),
279               600, 500);
280     } catch (OutOfMemoryError oom)
281     {
282       new OOMWarning((exportFeatures ? MessageManager.formatMessage(
283               "label.generating_features_for_params", new String[]
284               { ap.alignFrame.getTitle() })
285               : MessageManager.formatMessage(
286                       "label.generating_annotations_for_params",
287                       new String[]
288                       { ap.alignFrame.getTitle() })),
289               oom);
290       cap.dispose();
291     }
292
293     close_actionPerformed();
294   }
295
296   private void close_actionPerformed()
297   {
298     try
299     {
300       frame.setClosed(true);
301     } catch (java.beans.PropertyVetoException ex)
302     {
303     }
304   }
305
306   /**
307    * Adds widgets to the panel
308    * 
309    * @throws Exception
310    */
311   private void jbInit() throws Exception
312   {
313     this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
314     this.setBackground(Color.white);
315
316     JPanel formatPanel = buildFormatOptionsPanel();
317     JPanel linkedFeatures = buildLinkedFeaturesPanel();
318     JPanel actionsPanel = buildActionsPanel();
319
320     this.add(formatPanel);
321     this.add(linkedFeatures);
322     this.add(actionsPanel);
323   }
324
325   /**
326    * Builds a panel with a checkbox for the option to export linked (CDS/peptide)
327    * features. This is hidden by default, and only made visible if exporting
328    * features from a split frame panel which is configured to show linked
329    * features.
330    * 
331    * @return
332    */
333   private JPanel buildLinkedFeaturesPanel()
334   {
335     linkedFeaturesPanel = new JPanel();
336     linkedFeaturesPanel.setOpaque(false);
337
338     boolean nucleotide = ap.av.isNucleotide();
339     String complement = nucleotide
340             ? MessageManager.getString("label.protein").toLowerCase()
341             : "CDS";
342     JLabel label = new JLabel(
343             MessageManager.formatMessage("label.include_linked_features",
344                     complement));
345     label.setHorizontalAlignment(SwingConstants.TRAILING);
346     String tooltip = MessageManager
347             .formatMessage("label.include_linked_tooltip", complement);
348     label.setToolTipText(
349             JvSwingUtils.wrapTooltip(true, tooltip));
350
351     includeLinkedFeatures = new JCheckBox();
352     linkedFeaturesPanel.add(label);
353     linkedFeaturesPanel.add(includeLinkedFeatures);
354     linkedFeaturesPanel.setVisible(false);
355
356     return linkedFeaturesPanel;
357   }
358
359   /**
360    * Builds the panel with to File or Textbox or Close actions
361    * 
362    * @return
363    */
364   JPanel buildActionsPanel()
365   {
366     JPanel actionsPanel = new JPanel();
367     actionsPanel.setOpaque(false);
368
369     JButton toFile = new JButton(MessageManager.getString("label.to_file"));
370     toFile.addActionListener(new ActionListener()
371     {
372       @Override
373       public void actionPerformed(ActionEvent e)
374       {
375         toFile_actionPerformed();
376       }
377     });
378     JButton toTextbox = new JButton(
379             MessageManager.getString("label.to_textbox"));
380     toTextbox.addActionListener(new ActionListener()
381     {
382       @Override
383       public void actionPerformed(ActionEvent e)
384       {
385         toTextbox_actionPerformed();
386       }
387     });
388     JButton close = new JButton(MessageManager.getString("action.close"));
389     close.addActionListener(new ActionListener()
390     {
391       @Override
392       public void actionPerformed(ActionEvent e)
393       {
394         close_actionPerformed();
395       }
396     });
397
398     actionsPanel.add(toFile);
399     actionsPanel.add(toTextbox);
400     actionsPanel.add(close);
401
402     return actionsPanel;
403   }
404
405   /**
406    * Builds the panel with options to output in Jalview, GFF or CSV format. GFF is
407    * only made visible when exporting features, CSV only when exporting
408    * annotation.
409    * 
410    * @return
411    */
412   JPanel buildFormatOptionsPanel()
413   {
414     JPanel formatPanel = new JPanel();
415     // formatPanel.setBorder(BorderFactory.createEtchedBorder());
416     formatPanel.setOpaque(false);
417
418     JRadioButton jalviewFormat = new JRadioButton("Jalview");
419     jalviewFormat.setOpaque(false);
420     jalviewFormat.setSelected(true);
421     GFFFormat.setOpaque(false);
422     GFFFormat.setText("GFF");
423     CSVFormat.setOpaque(false);
424     CSVFormat.setText(MessageManager.getString("label.csv_spreadsheet"));
425
426     ButtonGroup buttonGroup = new ButtonGroup();
427     buttonGroup.add(jalviewFormat);
428     buttonGroup.add(GFFFormat);
429     buttonGroup.add(CSVFormat);
430
431     JLabel format = new JLabel(
432             MessageManager.getString("action.format") + " ");
433     format.setHorizontalAlignment(SwingConstants.TRAILING);
434
435     formatPanel.add(format);
436     formatPanel.add(jalviewFormat);
437     formatPanel.add(GFFFormat);
438     formatPanel.add(CSVFormat);
439
440     return formatPanel;
441   }
442 }