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