001/* A tableau representing a Web Browser window. 002 003 Copyright (c) 2002-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.actor.gui; 028 029import java.io.IOException; 030 031import ptolemy.kernel.util.IllegalActionException; 032import ptolemy.kernel.util.InvalidStateException; 033import ptolemy.kernel.util.KernelException; 034import ptolemy.kernel.util.NameDuplicationException; 035import ptolemy.kernel.util.Nameable; 036import ptolemy.kernel.util.NamedObj; 037 038/////////////////////////////////////////////////////////////////// 039//// BrowserTableau 040 041/** 042 A tableau representing a web browser window. 043 044 There can be any number of instances of this class in an effigy. 045 046 @author Christopher Hylands 047 @version $Id$ 048 @since Ptolemy II 2.0 049 @Pt.ProposedRating Yellow (cxh) 050 @Pt.AcceptedRating Red (cxh) 051 @see BrowserEffigy 052 @see BrowserLauncher 053 */ 054public class BrowserTableau extends Tableau { 055 /** Construct a new tableau for the model represented by the given effigy. 056 * @param container The container. 057 * @param name The name. 058 * @exception IllegalActionException If the container does not accept 059 * this entity (this should not occur). 060 * @exception NameDuplicationException If the name coincides with an 061 * attribute already in the container. 062 */ 063 public BrowserTableau(BrowserEffigy container, String name) 064 throws IllegalActionException, NameDuplicationException { 065 super(container, name); 066 } 067 068 /////////////////////////////////////////////////////////////////// 069 //// public methods //// 070 071 /** Make the tableau editable or uneditable. Notice that this does 072 * not change whether the effigy is modifiable, so other tableaux 073 * on the same effigy may still modify the associated file. 074 * @param flag False to make the tableau uneditable. 075 */ 076 @Override 077 public void setEditable(boolean flag) { 078 super.setEditable(flag); 079 } 080 081 /** Make this tableau visible by calling 082 * {@link BrowserLauncher#openURL(String)} 083 * with URI from the effigy. Most browsers are smart enough 084 * so that if the browser is already displaying the URI, then 085 * that window will be brought to the foreground. We are limited 086 * by the lack of communication between Java and the browser, 087 * so this is the best we can do. 088 * If the URI ends in "#in_browser", we strip it off before 089 * passing the URI to the browser. #in_browser is used by 090 * {@link ptolemy.actor.gui.HTMLViewer} to force a hyperlink to be 091 * opened in a browser. 092 */ 093 @Override 094 public void show() { 095 // FIXME: Unfortunately, the _config.showAll() at the bottom 096 // of MoMLApplication.parseArgs() will end up calling this method 097 // a second time. 098 // FIXME: Probably the following could make better use of URI 099 // facilities (used to be URL based). 100 String url = ((Effigy) getContainer()).uri.getURI().toString(); 101 102 try { 103 if (url.startsWith("jar:")) { 104 // If the URL begins with jar: then we are inside Web 105 // Start, or the Windows installer // and we should 106 // get the resource, and try to write the file to the 107 // place where it would appear in the classpath. 108 // For example, if url is 109 // jar:file:/D:/ptII/doc/design.jar!/doc/design/design.pdf 110 // then we try to save the file as 111 // d:/ptII/doc/design.pdf 112 // if d:/ptII/doc is writable. 113 String temporaryURL = null; 114 115 try { 116 // We try to save the resource in the classpath, but 117 // if we fail, then we copy the resource to a temporary 118 // location. 119 // If we are successful, then note that the file 120 // that we create is not deleted when we exit. 121 temporaryURL = JNLPUtilities.saveJarURLInClassPath(url); 122 } catch (Exception ex) { 123 // We print out the error and move on. 124 // Eventually, this could be logged as a warning. 125 System.out.println("Failed to save '" + url + "': " + ex); 126 ex.printStackTrace(); 127 } 128 129 if (temporaryURL != null) { 130 url = temporaryURL; 131 } else { 132 // For some reason we could not write the file, so 133 // save the jar file as a temporary file in the default 134 // platform dependent directory with the same suffix 135 // as that of the jar URL. 136 // In this case, the temporary file is deleted when 137 // we exit. 138 url = JNLPUtilities.saveJarURLAsTempFile(url, "tmp", null, 139 null); 140 } 141 } 142 143 String inBrowser = "#in_browser"; 144 145 if (url.endsWith(inBrowser)) { 146 // Strip off any trailing #in_browser, see HTMLViewer. 147 url = url.substring(0, url.length() - inBrowser.length()); 148 } 149 150 inBrowser = "%23in_browser"; 151 if (url.endsWith(inBrowser)) { 152 // Strip off any trailing #in_browser, see HTMLViewer. 153 url = url.substring(0, url.length() - inBrowser.length()); 154 } 155 156 BrowserLauncher.openURL(url); 157 158 try { 159 // We set the container to null immediately because 160 // once we spawn the browser process, we have no 161 // way of communicating with it, so we have no way 162 // of knowing when the browser has been closed. 163 // 164 // FIXME: this effectively destroys the Tableau/Effigy model 165 // for BrowserTableaus, but there is not much to be done 166 // about it since we do not have a platform independent way 167 // of communicating with the browser that we invoke. 168 setContainer(null); 169 } catch (KernelException ex2) { 170 throw new InvalidStateException((Nameable) null, ex2, 171 "setContainer(null) failed, url was " + url); 172 } 173 } catch (IOException ex) { 174 throw new InvalidStateException((Nameable) null, ex, 175 "Failed to handle '" + url + "': "); 176 } 177 } 178 179 /////////////////////////////////////////////////////////////////// 180 //// inner classes //// 181 182 /** A factory that creates web browser tableaux for Ptolemy models. 183 */ 184 public static class Factory extends TableauFactory { 185 /** Create a factory with the given name and container. 186 * @param container The container entity. 187 * @param name The name of the entity. 188 * @exception IllegalActionException If the container is incompatible 189 * with this attribute. 190 * @exception NameDuplicationException If the name coincides with 191 * an attribute already in the container. 192 */ 193 public Factory(NamedObj container, String name) 194 throws IllegalActionException, NameDuplicationException { 195 super(container, name); 196 } 197 198 /////////////////////////////////////////////////////////////////// 199 //// public methods //// 200 201 /** If the specified effigy is a BrowserEffigy and it 202 * already contains a tableau named 203 * "browserTableau", then return that tableau; otherwise, create 204 * a new instance of BrowserTableau in the specified 205 * effigy, and name it "browserTableau" and return that tableau. 206 * If the specified 207 * effigy is not an instance of BrowserEffigy, then do not 208 * create a tableau and return null. It is the 209 * responsibility of callers of this method to check the 210 * return value and call show(). 211 * @param effigy The effigy. 212 * @return A browser editor tableau, or null if one cannot be 213 * found or created. 214 * @exception Exception If the factory should be able to create a 215 * tableau for the effigy, but something goes wrong. 216 */ 217 @Override 218 public Tableau createTableau(Effigy effigy) throws Exception { 219 if (effigy instanceof BrowserEffigy) { 220 // First see whether the effigy already contains a 221 // BrowserTableau with the appropriate name. 222 BrowserTableau tableau = (BrowserTableau) effigy 223 .getEntity("browserTableau"); 224 225 if (tableau == null) { 226 tableau = new BrowserTableau((BrowserEffigy) effigy, 227 "browserTableau"); 228 } 229 230 tableau.setEditable(effigy.isModifiable()); 231 return tableau; 232 } else { 233 return null; 234 } 235 } 236 } 237}