JAL-3048 all image export (alignment, tree, PCA, Jmol) now via ImageExporter and...
[jalview.git] / src / jalview / gui / ImageExporter.java
1 package jalview.gui;
2
3 import jalview.bin.Cache;
4 import jalview.bin.Jalview;
5 import jalview.io.JalviewFileChooser;
6 import jalview.io.JalviewFileView;
7 import jalview.util.ImageMaker;
8 import jalview.util.ImageMaker.TYPE;
9 import jalview.util.MessageManager;
10 import jalview.util.dialogrunner.RunResponse;
11
12 import java.awt.Component;
13 import java.awt.Graphics;
14 import java.io.File;
15 import java.util.concurrent.atomic.AtomicBoolean;
16
17 import javax.swing.JOptionPane;
18
19 /**
20  * A class that marshals steps in exporting a view in image graphics format
21  * <ul>
22  * <li>prompts the user for the output file, if not already specified</li>
23  * <li>prompts the user for Text or Lineart character rendering, if
24  * necessary</li>
25  * <li>instantiates an ImageMaker to create the appropriate Graphics output
26  * context for the image format</li>
27  * <li>invokes a callback to do the work of writing to the graphics</li>
28  * </ul>
29  * 
30  * @author gmcarstairs
31  *
32  */
33 public class ImageExporter
34 {
35   // todo move interface to jalview.api? or replace with lambda?
36   /**
37    * An interface for the callback that can be run to write the image on to the
38    * graphics object. The callback should throw any exceptions arising so they
39    * can be reported by this class.
40    */
41   public interface ImageWriterI
42   {
43     void exportImage(Graphics g)
44             throws Exception;
45   }
46
47   private IProgressIndicator messageBoard;
48
49   private ImageWriterI imageWriter;
50
51   TYPE imageType;
52
53   private String title;
54
55   /**
56    * Constructor given a callback handler to write graphics data, an (optional)
57    * target for status messages, image type and (optional) title for output file
58    * 
59    * @param writer
60    * @param statusBar
61    * @param type
62    * @param fileTitle
63    */
64   public ImageExporter(ImageWriterI writer, IProgressIndicator statusBar,
65           TYPE type, String fileTitle)
66   {
67     this.imageWriter = writer;
68     this.messageBoard = statusBar;
69     this.imageType = type;
70     this.title = fileTitle;
71   }
72
73   /**
74    * Prompts the user for output file and Text/Lineart options as required,
75    * configures a Graphics context for output, and makes a callback to the
76    * client code to perform the image output
77    * 
78    * @param file
79    *          output file (if null, user is prompted to choose)
80    * @param parent
81    *          parent component for any dialogs shown
82    * @param width
83    * @param height
84    * @param imageSource
85    *          what the image is of e.g. Tree, Alignment
86    */
87   public void doExport(File file, Component parent, int width, int height,
88           String imageSource)
89   {
90     final long messageId = System.currentTimeMillis();
91
92     /*
93      * prompt user for output file if not provided
94      */
95     if (file == null && !Jalview.isHeadlessMode())
96     {
97       JalviewFileChooser chooser = imageType.getFileChooser();
98       chooser.setFileView(new JalviewFileView());
99       MessageManager.formatMessage("label.create_image_of",
100               imageType.getName(), imageSource);
101       String title = "Create " + imageType.getName()
102               + " image from alignment";
103       chooser.setDialogTitle(title);
104       chooser.setToolTipText(MessageManager.getString("action.save"));
105       int value = chooser.showSaveDialog(parent);
106       if (value != JalviewFileChooser.APPROVE_OPTION)
107       {
108         String msg = MessageManager.formatMessage(
109                 "status.cancelled_image_export_operation", imageType.name);
110         setStatus(msg, messageId);
111         return;
112       }
113       Cache.setProperty("LAST_DIRECTORY",
114               chooser.getSelectedFile().getParent());
115       file = chooser.getSelectedFile();
116     }
117
118     /*
119      * Prompt for Text or Lineart (EPS/SVG) unless a preference is already set
120      * for this as EPS_RENDERING / SVG_RENDERING
121      */
122     String renderStyle = Cache.getDefault(
123             imageType.getName() + "_RENDERING",
124             LineartOptions.PROMPT_EACH_TIME);
125     AtomicBoolean textSelected = new AtomicBoolean(
126             !"Lineart".equals(renderStyle));
127     if ((imageType == TYPE.EPS || imageType == TYPE.SVG)
128             && LineartOptions.PROMPT_EACH_TIME.equals(renderStyle)
129             && !Jalview.isHeadlessMode())
130     {
131       final File chosenFile = file;
132       RunResponse okAction = new RunResponse(JOptionPane.OK_OPTION)
133       {
134         @Override
135         public void run()
136         {
137           exportImage(chosenFile, !textSelected.get(), width, height,
138                   messageId);
139         }
140       };
141       LineartOptions epsOption = new LineartOptions(TYPE.EPS.getName(),
142               textSelected);
143       epsOption.setResponseAction(new RunResponse(JOptionPane.NO_OPTION)
144       {
145         @Override
146         public void run()
147         {
148           setStatus(MessageManager.formatMessage(
149                   "status.cancelled_image_export_operation",
150                   imageType.getName()), messageId);
151         }
152       });
153       epsOption.setResponseAction(okAction);
154       epsOption.showDialog();
155       /* no code here - JalviewJS cannot execute it */
156     }
157     else
158     {
159       /*
160        * character rendering not required, or preference already set 
161        * - just do the export
162        */
163       exportImage(file, !textSelected.get(), width, height, messageId);
164     }
165   }
166
167   /**
168    * Constructs a suitable graphics context and passes it to the callback
169    * handler for the image to be written. Shows status messages for export in
170    * progress, complete, or failed as appropriate.
171    * 
172    * @param chosenFile
173    * @param asLineart
174    * @param width
175    * @param height
176    * @param messageId
177    */
178   protected void exportImage(File chosenFile, boolean asLineart, int width,
179           int height, long messageId)
180   {
181     String type = imageType.getName();
182     try
183     {
184       setStatus(
185               MessageManager.formatMessage(
186                       "status.exporting_alignment_as_x_file", type),
187               messageId);
188       ImageMaker im = new ImageMaker(imageType, width, height, chosenFile,
189               title, asLineart);
190       imageWriter.exportImage(im.getGraphics());
191       im.writeImage();
192       setStatus(
193               MessageManager.formatMessage("status.export_complete", type),
194               messageId);
195     } catch (Exception e)
196     {
197       System.out
198               .println(String.format("Error creating %s file: %s", type,
199                       e.toString()));
200       setStatus(MessageManager.formatMessage("info.error_creating_file",
201               type), messageId);
202     }
203   }
204
205   /**
206    * Asks the callback to show a status message with given id
207    * 
208    * @param msg
209    * @param id
210    */
211   void setStatus(String msg, long id)
212   {
213     if (messageBoard != null)
214     {
215       messageBoard.setProgressBar(msg, id);
216     }
217   }
218
219 }