90b212f06a24ba4dfbfbb9ab80032dc5c39dc5c6
[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     setStatus(
92             MessageManager.formatMessage(
93                     "status.exporting_alignment_as_x_file", imageType),
94             messageId);
95
96     /*
97      * prompt user for output file if not provided
98      */
99     if (file == null && !Jalview.isHeadlessMode())
100     {
101       JalviewFileChooser chooser = imageType.getFileChooser();
102       chooser.setFileView(new JalviewFileView());
103       MessageManager.formatMessage("label.create_image_of",
104               imageType.getName(), imageSource);
105       String title = "Create " + imageType.getName()
106               + " image from alignment";
107       chooser.setDialogTitle(title);
108       chooser.setToolTipText(MessageManager.getString("action.save"));
109       int value = chooser.showSaveDialog(parent);
110       if (value != JalviewFileChooser.APPROVE_OPTION)
111       {
112         String msg = MessageManager.formatMessage(
113                 "status.cancelled_image_export_operation", imageType.name);
114         setStatus(msg, messageId);
115         return;
116       }
117       Cache.setProperty("LAST_DIRECTORY",
118               chooser.getSelectedFile().getParent());
119       file = chooser.getSelectedFile();
120     }
121
122     /*
123      * Prompt for Text or Lineart (EPS/SVG) unless a preference is already set
124      * for this as EPS_RENDERING / SVG_RENDERING
125      * Always set to Text for JalviewJS as Lineart (glyph fonts) not available
126      */
127     String renderStyle = Cache.getDefault(
128             imageType.getName() + "_RENDERING",
129             LineartOptions.PROMPT_EACH_TIME);
130     if (Jalview.isJS())
131     {
132       renderStyle = "Text";
133     }
134     AtomicBoolean textSelected = new AtomicBoolean(
135             !"Lineart".equals(renderStyle));
136     if ((imageType == TYPE.EPS || imageType == TYPE.SVG)
137             && LineartOptions.PROMPT_EACH_TIME.equals(renderStyle)
138             && !Jalview.isHeadlessMode())
139     {
140       final File chosenFile = file;
141       RunResponse okAction = new RunResponse(JOptionPane.OK_OPTION)
142       {
143         @Override
144         public void run()
145         {
146           exportImage(chosenFile, !textSelected.get(), width, height,
147                   messageId);
148         }
149       };
150       LineartOptions epsOption = new LineartOptions(TYPE.EPS.getName(),
151               textSelected);
152       epsOption.setResponseAction(1, new RunResponse(JOptionPane.NO_OPTION)
153       {
154         @Override
155         public void run()
156         {
157           setStatus(MessageManager.formatMessage(
158                   "status.cancelled_image_export_operation",
159                   imageType.getName()), messageId);
160         }
161       });
162       epsOption.setResponseAction(0, okAction);
163       epsOption.showDialog();
164       /* no code here - JalviewJS cannot execute it */
165     }
166     else
167     {
168       /*
169        * character rendering not required, or preference already set 
170        * - just do the export
171        */
172       exportImage(file, !textSelected.get(), width, height, messageId);
173     }
174   }
175
176   /**
177    * Constructs a suitable graphics context and passes it to the callback
178    * handler for the image to be written. Shows status messages for export in
179    * progress, complete, or failed as appropriate.
180    * 
181    * @param chosenFile
182    * @param asLineart
183    * @param width
184    * @param height
185    * @param messageId
186    */
187   protected void exportImage(File chosenFile, boolean asLineart, int width,
188           int height, long messageId)
189   {
190     String type = imageType.getName();
191     try
192     {
193 //      setStatus(
194 //              MessageManager.formatMessage(
195 //                      "status.exporting_alignment_as_x_file", type),
196 //              messageId);
197       ImageMaker im = new ImageMaker(imageType, width, height, chosenFile,
198               title, asLineart);
199       imageWriter.exportImage(im.getGraphics());
200       im.writeImage();
201       setStatus(
202               MessageManager.formatMessage("status.export_complete", type),
203               messageId);
204     } catch (Exception e)
205     {
206       System.out
207               .println(String.format("Error creating %s file: %s", type,
208                       e.toString()));
209       setStatus(MessageManager.formatMessage("info.error_creating_file",
210               type), messageId);
211     }
212   }
213
214   /**
215    * Asks the callback to show a status message with given id
216    * 
217    * @param msg
218    * @param id
219    */
220   void setStatus(String msg, long id)
221   {
222     if (messageBoard != null && !Jalview.isHeadlessMode())
223     {
224       messageBoard.setProgressBar(msg, id);
225     }
226   }
227
228 }