001 /* 002 * CRIMSON 003 * Copyright (c) 2006, Stephen Fisher, Susan Davidson, and Junhyong Kim, 004 * University of Pennsylvania. 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License as 008 * published by the Free Software Foundation; either version 2 of the 009 * License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, but 012 * WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 * 02110-1301 USA. 020 * 021 * @(#)ViewHTML.java 022 */ 023 024 package edu.upenn.crimson.gui; 025 026 import edu.upenn.crimson.CrimsonUtils; 027 import java.awt.*; 028 import java.awt.event.*; 029 import javax.swing.*; 030 import javax.swing.text.html.*; 031 import javax.swing.event.*; 032 import java.net.URL; 033 import java.io.*; 034 import javax.swing.filechooser.FileFilter; 035 036 /** 037 * Browse HTML file or text. 038 * 039 * @author Stephen Fisher 040 * @version $Id: ViewHTML.java,v 1.4 2007/05/16 18:55:58 fisher Exp $ 041 */ 042 043 public class ViewHTML extends JFrame { 044 private final static int URL = 1; 045 private final static int TEXT = 2; 046 private final static int INPUTSTREAM = 3; 047 048 ViewHTML thisFrame; 049 HTMLEditorPane htmlText = new HTMLEditorPane(); 050 051 public ViewHTML(URL source) { 052 super("CRIMSON: HTML Viewer"); 053 setup(source, URL); 054 } 055 056 public ViewHTML(String source) { 057 super("CRIMSON: HTML Viewer"); 058 setup(source, TEXT); 059 } 060 061 public ViewHTML(InputStream source) { 062 super("CRIMSON: HTML Viewer"); 063 setup(source, INPUTSTREAM); 064 } 065 066 private void setup(Object source, int type) { 067 // keep pointer to self so can 'dispose' Frame below 068 thisFrame = this; 069 070 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 071 072 // setup text area to display data 073 htmlText.setEditable(false); 074 htmlText.addHyperlinkListener(new Hyperactive()); 075 switch (type) { 076 case URL: 077 try { 078 htmlText.setPage((URL) source); 079 } catch (IOException e) { 080 CrimsonUtils.printMsg("Invalid URL to be displayed", CrimsonUtils.ERROR); 081 return; 082 } 083 break; 084 case TEXT: 085 htmlText.setContentType("text/html"); 086 htmlText.setText((String) source); 087 break; 088 case INPUTSTREAM: 089 htmlText.setContentType("text/html"); 090 try { 091 htmlText.read((InputStream) source, null); 092 } catch (IOException e) { 093 CrimsonUtils.printMsg("Invalid InputStream to be displayed", CrimsonUtils.ERROR); 094 return; 095 } 096 break; 097 } 098 JScrollPane htmlTextSP = new JScrollPane(htmlText); 099 htmlTextSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 100 101 // button panel 102 JPanel buttonP = new JPanel(new GridLayout(1,0)); 103 buttonP.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 104 // save button 105 JButton saveB = new JButton("Save HTML"); 106 saveB.addActionListener(new ActionListener() { 107 public void actionPerformed(ActionEvent e) { 108 String filename = saveFileChooser(); 109 if (filename.length() > 0) { 110 // set overwrite to 'true' because the user 111 // agreed to overwrite the file in the 112 // FileChooser 113 saveText(filename, true); 114 } 115 } 116 }); 117 buttonP.add(saveB); 118 // close button 119 JButton closeB = new JButton("Close Viewer"); 120 closeB.addActionListener(new ActionListener() { 121 public void actionPerformed(ActionEvent e) { 122 thisFrame.dispose(); 123 } 124 }); 125 buttonP.add(closeB); 126 127 getContentPane().setLayout(new BorderLayout()); 128 getContentPane().add(htmlTextSP, BorderLayout.CENTER); 129 getContentPane().add(buttonP, BorderLayout.SOUTH); 130 pack(); 131 132 // set the default window size 133 setSize(800, 900); 134 135 // display the window 136 setVisible(true); 137 } 138 139 private class Hyperactive implements HyperlinkListener { 140 public void hyperlinkUpdate(HyperlinkEvent e) { 141 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 142 HTMLEditorPane pane = (HTMLEditorPane) e.getSource(); 143 if (e instanceof HTMLFrameHyperlinkEvent) { 144 HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e; 145 HTMLDocument doc = (HTMLDocument)pane.getDocument(); 146 doc.processHTMLFrameHyperlinkEvent(evt); 147 } else { 148 try { 149 pane.setPage(e.getURL()); 150 } catch (Throwable t) { 151 t.printStackTrace(); 152 } 153 } 154 } 155 } 156 } 157 158 /** 159 * Use a JFileChooser the get the file info for saving HTML to a file. 160 */ 161 public String saveFileChooser() { 162 // use the current working directory 163 JFileChooser fileChooser = new JFileChooser(); 164 165 // set the title 166 fileChooser.setDialogTitle("Save HTML File"); 167 // set the filter, if present 168 fileChooser.setAcceptAllFileFilterUsed(true); 169 FileFilter filter = new HTMLFilter(); 170 fileChooser.addChoosableFileFilter(filter); 171 fileChooser.setFileFilter(filter); 172 173 // launch the file chooser 174 int status = fileChooser.showSaveDialog(null); 175 if (status == JFileChooser.APPROVE_OPTION) { 176 File file = fileChooser.getSelectedFile(); 177 String filename = GUIUtils.getFilename(file); 178 179 String[] ext = {".htm", ".html"}; 180 boolean notFound = true; 181 int i = 0; 182 while (notFound && (i < ext.length)) { 183 if (filename.endsWith(ext[i])) notFound = false; 184 i++; 185 } 186 187 // the filename doesn't end with a valid ext, so loop 188 // through the extensions to see if we can find a file 189 // with one of the valid extensions. we could just take 190 // the first ext that isn't a valid file but it's assumed 191 // that users will be consistent in their use of 192 // extensions and thus if ".html" matches but ".htm" 193 // doesn't, we assume that ".html" is actually what the 194 // user wants to use. 195 if (notFound) { 196 i = 0; 197 // we're overloading 'notFound' by using it 198 // here as well as above. 199 while (notFound && (i < ext.length)) { 200 file = new File(filename + ext[i]); 201 if (file.exists()) notFound = false; 202 else i++; // don't increment if found 203 } 204 // if no file extensions match a file, then 205 // just use the first one in the list. 206 if (notFound) filename += ext[0]; 207 else filename += ext[i]; 208 } 209 210 if (! file.exists()) return filename; 211 212 // the file exist, so check if want to overwrite the file 213 String msg = "The file \"" + file.getPath() + "\" already exists.\n"; 214 msg += "Do you want to overwrite the file?"; 215 Object[] options = {"Overwrite", "Cancel"}; 216 int flag = JOptionPane.showOptionDialog(null, msg, 217 "Overwrite Confirmation", 218 JOptionPane.YES_NO_OPTION, 219 JOptionPane.QUESTION_MESSAGE, 220 null, 221 options, 222 options[1]); 223 if (flag == JOptionPane.YES_OPTION) { // "Overwrite" 224 return filename; 225 } else { 226 return saveFileChooser(); 227 } 228 } 229 230 return ""; 231 } 232 233 /** 234 * Save the html text to a file. 235 * 236 * @XXX need to throw FileIO exceptions, rather than just print 237 * errors. 238 */ 239 public void saveText(String filename, boolean overwrite) { 240 // add ".htm" filename extension, if necessary 241 if ((! filename.endsWith(".html")) && (! filename.endsWith(".htm"))) { 242 filename += ".htm"; 243 } 244 245 File file = new File(filename); 246 // if the file already exists and not supposed to overwrite 247 // it, then return on error. 248 if (file.exists() && (! overwrite)) { 249 CrimsonUtils.printMsg("ERROR: file \"" + filename + "\" already exists."); 250 return; 251 } 252 253 try { 254 FileWriter fWriter = new FileWriter(file); 255 BufferedWriter bWriter = new BufferedWriter(fWriter); 256 257 // bWriter.write(htmlText.getText()); 258 bWriter.write(htmlText.getHTML()); 259 // bWriter.newLine(); 260 bWriter.flush(); 261 bWriter.close(); 262 } catch (FileNotFoundException e) { 263 // problem with FileOutputStream 264 CrimsonUtils.printMsg("File \"" + filename + "\" can not be opened.", 265 CrimsonUtils.ERROR); 266 } catch (IOException e) { 267 // problem with ObjectOutputStream. XXX do we need to 268 // close bWriter()? 269 CrimsonUtils.printMsg("Error writting html file \"" + filename + "\".", 270 CrimsonUtils.ERROR); 271 } 272 } 273 274 /** 275 * HTML FileFilter. 276 */ 277 private class HTMLFilter extends FileFilter { 278 public boolean accept(File f) { 279 // accept directories 280 if (f.isDirectory()) return true; 281 282 // accept all files 283 if ((f.getName()).endsWith(".html")) return true; 284 if ((f.getName()).endsWith(".htm")) return true; 285 286 return false; 287 } 288 289 // set the filter's description 290 public String getDescription() { return "HTML files (*.htm; *.html)"; } 291 } 292 293 /** 294 * This class is meant to wrap the JEditorPane because the 295 * JEditorPane munges up the HTML source when it stores the text. 296 * When it stores HTML it looses tags that it doesn't know about 297 * and thus when we use getText() routine to get the original HTML 298 * text back it isn't correct. So here we keep a copy of the 299 * original HTML. 300 * @XXX This should also handle URLs and InputStreams. 301 */ 302 private class HTMLEditorPane extends JEditorPane { 303 private String origHTML = ""; 304 305 public void setText(String source) { 306 super.setText(source); 307 origHTML = source; 308 } 309 310 public String getHTML() { 311 if (origHTML.length() > 0) return origHTML; 312 else return getText(); 313 } 314 } 315 316 } // ViewHTML.java