JAL-3048 wip externalise trigger from RunResponse
[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      * Always set to Text for JalviewJS as Lineart (glyph fonts) not available
122      */
123     String renderStyle = Cache.getDefault(
124             imageType.getName() + "_RENDERING",
125             LineartOptions.PROMPT_EACH_TIME);
126     if (Jalview.isJS())
127     {
128       renderStyle = "Text";
129     }
130     AtomicBoolean textSelected = new AtomicBoolean(
131             !"Lineart".equals(renderStyle));
132     if ((imageType == TYPE.EPS || imageType == TYPE.SVG)
133             && LineartOptions.PROMPT_EACH_TIME.equals(renderStyle)
134             && !Jalview.isHeadlessMode())
135     {
136       final File chosenFile = file;
137       RunResponse okAction = new RunResponse(JOptionPane.OK_OPTION)
138       {
139         @Override
140         public void run()
141         {
142           exportImage(chosenFile, !textSelected.get(), width, height,
143                   messageId);
144         }
145       };
146       LineartOptions epsOption = new LineartOptions(TYPE.EPS.getName(),
147               textSelected);
148       epsOption.setResponseAction(1, new RunResponse(JOptionPane.NO_OPTION)
149       {
150         @Override
151         public void run()
152         {
153           setStatus(MessageManager.formatMessage(
154                   "status.cancelled_image_export_operation",
155                   imageType.getName()), messageId);
156         }
157       });
158       epsOption.setResponseAction(0, okAction);
159       epsOption.showDialog();
160       /* no code here - JalviewJS cannot execute it */
161     }
162     else
163     {
164       /*
165        * character rendering not required, or preference already set 
166        * - just do the export
167        */
168       exportImage(file, !textSelected.get(), width, height, messageId);
169     }
170   }
171
172   /**
173    * Constructs a suitable graphics context and passes it to the callback
174    * handler for the image to be written. Shows status messages for export in
175    * progress, complete, or failed as appropriate.
176    * 
177    * @param chosenFile
178    * @param asLineart
179    * @param width
180    * @param height
181    * @param messageId
182    */
183   protected void exportImage(File chosenFile, boolean asLineart, int width,
184           int height, long messageId)
185   {
186     String type = imageType.getName();
187     try
188     {
189       setStatus(
190               MessageManager.formatMessage(
191                       "status.exporting_alignment_as_x_file", type),
192               messageId);
193       ImageMaker im = new ImageMaker(imageType, width, height, chosenFile,
194               title, asLineart);
195       imageWriter.exportImage(im.getGraphics());
196       im.writeImage();
197       setStatus(
198               MessageManager.formatMessage("status.export_complete", type),
199               messageId);
200     } catch (Exception e)
201     {
202       System.out
203               .println(String.format("Error creating %s file: %s", type,
204                       e.toString()));
205       setStatus(MessageManager.formatMessage("info.error_creating_file",
206               type), messageId);
207     }
208   }
209
210   /**
211    * Asks the callback to show a status message with given id
212    * 
213    * @param msg
214    * @param id
215    */
216   void setStatus(String msg, long id)
217   {
218     if (messageBoard != null && !Jalview.isHeadlessMode())
219     {
220       messageBoard.setProgressBar(msg, id);
221     }
222   }
223
224 }