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