JAL-3068 tidy status bar messaging
[jalview.git] / src / jalview / io / HTMLOutput.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.io;
22
23 import jalview.api.AlignExportSettingsI;
24 import jalview.bin.Cache;
25 import jalview.datamodel.AlignExportSettingsAdapter;
26 import jalview.datamodel.AlignmentExportData;
27 import jalview.gui.AlignmentPanel;
28 import jalview.gui.IProgressIndicator;
29 import jalview.util.MessageManager;
30
31 import java.io.BufferedReader;
32 import java.io.File;
33 import java.io.IOException;
34 import java.io.InputStreamReader;
35 import java.net.URL;
36 import java.util.Objects;
37
38 public abstract class HTMLOutput implements Runnable
39 {
40   protected AlignmentPanel ap;
41
42   /*
43    * key for progress or status messages
44    */
45   protected long pSessionId;
46
47   /*
48    * (optional) place to write progress messages to
49    */
50   protected IProgressIndicator pIndicator;
51
52   protected File generatedFile;
53
54   String _bioJson = null;
55
56   private String description;
57
58   /**
59    * Constructor given an alignment panel (which should not be null)
60    * 
61    * @param ap
62    * @param desc 
63    */
64   public HTMLOutput(AlignmentPanel ap, String desc)
65   {
66     this.ap = ap;
67     this.pIndicator = ap.alignFrame;
68     this.description = desc;
69     this.pSessionId = System.currentTimeMillis();
70   }
71
72   /**
73    * Gets the BioJSON data as a string, with lazy evaluation (first time called
74    * only). If the output format is configured not to embed BioJSON, returns
75    * null.
76    * 
77    * @return
78    */
79   public String getBioJSONData()
80   {
81     if (!isEmbedData())
82     {
83       return null;
84     }
85     if (_bioJson == null)
86     {
87       AlignExportSettingsI options = new AlignExportSettingsAdapter(true);
88       AlignmentExportData exportData = ap.getAlignViewport()
89               .getAlignExportData(options);
90       _bioJson = new FormatAdapter(ap, options).formatSequences(
91               FileFormat.Json, exportData.getAlignment(),
92               exportData.getOmitHidden(), exportData.getStartEndPostions(),
93               ap.getAlignViewport().getAlignment().getHiddenColumns());
94     }
95
96     return _bioJson;
97   }
98
99   /**
100    * Read a template file content as string
101    * 
102    * @param file
103    *          - the file to be read
104    * @return File content as String
105    * @throws IOException
106    */
107   public static String readFileAsString(File file) throws IOException
108   {
109     InputStreamReader isReader = null;
110     BufferedReader buffReader = null;
111     StringBuilder sb = new StringBuilder();
112     Objects.requireNonNull(file, "File must not be null!");
113     @SuppressWarnings("deprecation")
114     URL url = file.toURL();
115     if (url != null)
116     {
117       try
118       {
119         isReader = new InputStreamReader(url.openStream());
120         buffReader = new BufferedReader(isReader);
121         String line;
122         String lineSeparator = System.getProperty("line.separator");
123         while ((line = buffReader.readLine()) != null)
124         {
125           sb.append(line).append(lineSeparator);
126         }
127
128       } catch (Exception ex)
129       {
130         ex.printStackTrace();
131       } finally
132       {
133         if (isReader != null)
134         {
135           isReader.close();
136         }
137
138         if (buffReader != null)
139         {
140           buffReader.close();
141         }
142       }
143     }
144     return sb.toString();
145   }
146
147   public static String getImageMapHTML()
148   {
149     return new String("<html>\n" + "<head>\n"
150             + "<script language=\"JavaScript\">\n"
151             + "var ns4 = document.layers;\n"
152             + "var ns6 = document.getElementById && !document.all;\n"
153             + "var ie4 = document.all;\n" + "offsetX = 0;\n"
154             + "offsetY = 20;\n" + "var toolTipSTYLE=\"\";\n"
155             + "function initToolTips()\n" + "{\n" + "  if(ns4||ns6||ie4)\n"
156             + "  {\n"
157             + "    if(ns4) toolTipSTYLE = document.toolTipLayer;\n"
158             + "    else if(ns6) toolTipSTYLE = document.getElementById(\"toolTipLayer\").style;\n"
159             + "    else if(ie4) toolTipSTYLE = document.all.toolTipLayer.style;\n"
160             + "    if(ns4) document.captureEvents(Event.MOUSEMOVE);\n"
161             + "    else\n" + "    {\n"
162             + "      toolTipSTYLE.visibility = \"visible\";\n"
163             + "      toolTipSTYLE.display = \"none\";\n" + "    }\n"
164             + "    document.onmousemove = moveToMouseLoc;\n" + "  }\n"
165             + "}\n" + "function toolTip(msg, fg, bg)\n" + "{\n"
166             + "  if(toolTip.arguments.length < 1) // hide\n" + "  {\n"
167             + "    if(ns4) toolTipSTYLE.visibility = \"hidden\";\n"
168             + "    else toolTipSTYLE.display = \"none\";\n" + "  }\n"
169             + "  else // show\n" + "  {\n"
170             + "    if(!fg) fg = \"#555555\";\n"
171             + "    if(!bg) bg = \"#FFFFFF\";\n" + "    var content =\n"
172             + "    '<table border=\"0\" cellspacing=\"0\" cellpadding=\"1\" bgcolor=\"' + fg + '\"><td>' +\n"
173             + "    '<table border=\"0\" cellspacing=\"0\" cellpadding=\"1\" bgcolor=\"' + bg + \n"
174             + "    '\"><td align=\"center\"><font face=\"sans-serif\" color=\"' + fg +\n"
175             + "    '\" size=\"-2\">&nbsp;' + msg +\n"
176             + "    '&nbsp;</font></td></table></td></table>';\n"
177             + "    if(ns4)\n" + "    {\n"
178             + "      toolTipSTYLE.document.write(content);\n"
179             + "      toolTipSTYLE.document.close();\n"
180             + "      toolTipSTYLE.visibility = \"visible\";\n" + "    }\n"
181             + "    if(ns6)\n" + "    {\n"
182             + "      document.getElementById(\"toolTipLayer\").innerHTML = content;\n"
183             + "      toolTipSTYLE.display='block'\n" + "    }\n"
184             + "    if(ie4)\n" + "    {\n"
185             + "      document.all(\"toolTipLayer\").innerHTML=content;\n"
186             + "      toolTipSTYLE.display='block'\n" + "    }\n" + "  }\n"
187             + "}\n" + "function moveToMouseLoc(e)\n" + "{\n"
188             + "  if(ns4||ns6)\n" + "  {\n" + "    x = e.pageX;\n"
189             + "    y = e.pageY;\n" + "  }\n" + "  else\n" + "  {\n"
190             + "    x = event.x + document.body.scrollLeft;\n"
191             + "    y = event.y + document.body.scrollTop;\n" + "  }\n"
192             + "  toolTipSTYLE.left = x + offsetX;\n"
193             + "  toolTipSTYLE.top = y + offsetY;\n" + "  return true;\n"
194             + "}\n" + "</script>\n" + "</head>\n" + "<body>\n"
195             + "<div id=\"toolTipLayer\" style=\"position:absolute; visibility: hidden\"></div>\n"
196             + "<script language=\"JavaScript\"><!--\n"
197             + "initToolTips(); //--></script>\n");
198
199   }
200
201   /**
202    * Prompts the user to choose an output file and returns the file path, or
203    * null on Cancel
204    * 
205    * @return
206    */
207   public String getOutputFile()
208   {
209     String selectedFile = null;
210
211     // TODO: JAL-3048 generate html rendered view (requires SvgGraphics and/or
212     // Jalview HTML rendering system- probably not required for Jalview-JS)
213     JalviewFileChooser jvFileChooser = new JalviewFileChooser("html",
214             "HTML files");
215     jvFileChooser.setFileView(new JalviewFileView());
216
217     jvFileChooser
218             .setDialogTitle(MessageManager.getString("label.save_as_html"));
219     jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
220
221     int fileChooserOpt = jvFileChooser.showSaveDialog(null);
222     if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
223     {
224       Cache.setProperty("LAST_DIRECTORY",
225               jvFileChooser.getSelectedFile().getParent());
226       selectedFile = jvFileChooser.getSelectedFile().getPath();
227     }
228
229     return selectedFile;
230   }
231
232   protected void setProgressMessage(String message)
233   {
234     if (pIndicator != null && !isHeadless())
235     {
236       pIndicator.setProgressBar(message, pSessionId);
237     }
238     else
239     {
240       System.out.println(message);
241     }
242   }
243
244   /**
245    * Answers true if HTML export is invoke in headless mode or false otherwise
246    * 
247    * @return
248    */
249   protected boolean isHeadless()
250   {
251     return System.getProperty("java.awt.headless") != null
252             && System.getProperty("java.awt.headless").equals("true");
253   }
254
255   /**
256    * This method provides implementation of consistent behaviour which should
257    * occur after a HTML file export. It MUST be called at the end of the
258    * exportHTML() method implementation.
259    */
260   protected void exportCompleted()
261   {
262     if (isLaunchInBrowserAfterExport() && !isHeadless())
263     {
264       try
265       {
266         jalview.util.BrowserLauncher
267                 .openURL("file:///" + getExportedFile());
268       } catch (IOException e)
269       {
270         e.printStackTrace();
271       }
272     }
273   }
274
275   /**
276    * if this answers true then BioJSON data will be embedded to the exported
277    * HTML file otherwise it won't be embedded.
278    * 
279    * @return
280    */
281   public abstract boolean isEmbedData();
282
283   /**
284    * if this answers true then the generated HTML file is opened for viewing in
285    * a browser after its generation otherwise it won't be opened in a browser
286    * 
287    * @return
288    */
289   public abstract boolean isLaunchInBrowserAfterExport();
290
291   /**
292    * handle to the generated HTML file
293    * 
294    * @return
295    */
296   public File getExportedFile()
297   {
298     return generatedFile;
299   }
300
301   public void exportHTML(String outputFile)
302   {
303     setProgressMessage(MessageManager.formatMessage(
304         "status.exporting_alignment_as_x_file", getDescription()));
305     try
306     {
307       if (outputFile == null)
308       {
309         /*
310          * prompt for output file
311          */
312         outputFile = getOutputFile();
313         if (outputFile == null)
314         {
315           setProgressMessage(MessageManager.formatMessage(
316                   "status.cancelled_image_export_operation",
317                   getDescription()));
318           return;
319         }
320       }
321       generatedFile = new File(outputFile);
322     } catch (Exception e)
323     {
324       setProgressMessage(MessageManager
325               .formatMessage("info.error_creating_file", getDescription()));
326       e.printStackTrace();
327       return;
328     }
329     new Thread(this).start();
330   
331   }
332
333   /**
334    * Answers a short description of the image format suitable for display in
335    * messages
336    * 
337    * @return
338    */
339   protected final String getDescription()
340   {
341         return description;
342   }
343 }