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