0001: /*
0002: * Control a web browser from your java application.
0003: * Copyright (C) 2001-2005 Stephen Ostermiller
0004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
0005: * Copyright (C) 2005 Johann N. Loefflmann <jonelo@jonelo.de>
0006: *
0007: * This program is free software; you can redistribute it and/or modify
0008: * it under the terms of the GNU General Public License as published by
0009: * the Free Software Foundation; either version 2 of the License, or
0010: * (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * See COPYING.TXT for details.
0018: */
0019: package com.Ostermiller.util;
0020:
0021: import java.awt.*;
0022: import java.awt.event.*;
0023: import java.io.*;
0024: import java.lang.reflect.Method;
0025: import java.net.*;
0026: import java.text.MessageFormat;
0027: import java.util.*;
0028: import javax.swing.*;
0029:
0030: /**
0031: * Allows URLs to be opened in the system browser on Windows and Unix.
0032: * More information about this class is available from <a target="_top" href=
0033: * "http://ostermiller.org/utils/Browser.html">ostermiller.org</a>.
0034: *
0035: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
0036: * @since ostermillerutils 1.00.00
0037: */
0038: public class Browser {
0039:
0040: /**
0041: * The dialog that allows user configuration of the options for this class.
0042: *
0043: * @since ostermillerutils 1.00.00
0044: */
0045: protected static BrowserDialog dialog;
0046:
0047: /**
0048: * Locale specific strings displayed to the user.
0049: *
0050: * @since ostermillerutils 1.00.00
0051: */
0052: protected static ResourceBundle labels = ResourceBundle.getBundle(
0053: "com.Ostermiller.util.Browser", Locale.getDefault());
0054:
0055: /**
0056: * Set the locale used for getting localized
0057: * strings.
0058: *
0059: * @param locale Locale used to for i18n.
0060: *
0061: * @since ostermillerutils 1.00.00
0062: */
0063: public static void setLocale(Locale locale) {
0064: labels = ResourceBundle.getBundle(
0065: "com.Ostermiller.util.Browser", locale);
0066: }
0067:
0068: /**
0069: * A list of commands to try in order to display the url.
0070: * The url is put into the command using MessageFormat, so
0071: * the URL will be specified as {0} in the command.
0072: * Some examples of commands to try might be:<br>
0073: * <code>rundll32 url.dll,FileProtocolHandler {0}</code></br>
0074: * <code>netscape {0}</code><br>
0075: * These commands are passed in order to exec until something works
0076: * when displayURL is used.
0077: *
0078: * @since ostermillerutils 1.00.00
0079: */
0080: public static String[] exec = null;
0081:
0082: /**
0083: * Determine appropriate commands to start a browser on the current
0084: * operating system. On windows: <br>
0085: * <code>rundll32 url.dll,FileProtocolHandler {0}</code></br>
0086: * On other operating systems, the "which" command is used to
0087: * test if Mozilla, netscape, and lynx(xterm) are available (in that
0088: * order).
0089: *
0090: * @since ostermillerutils 1.00.00
0091: */
0092: public static void init() {
0093: exec = defaultCommands();
0094: }
0095:
0096: /**
0097: * Retrieve the default commands to open a browser for this system.
0098: * @return list of commands
0099: *
0100: * @since ostermillerutils 1.00.00
0101: */
0102: public static String[] defaultCommands() {
0103: String[] execLocal = null;
0104: if (System.getProperty("os.name").startsWith("Windows")) {
0105: execLocal = new String[] { "rundll32 url.dll,FileProtocolHandler {0}", };
0106: } else if (System.getProperty("os.name").startsWith("Mac")) {
0107: ArrayList<String> browsers = new ArrayList<String>();
0108: try {
0109: Process p = Runtime.getRuntime().exec("which open");
0110: if (p.waitFor() == 0) {
0111: browsers.add("open {0}");
0112: }
0113: } catch (IOException e) {
0114: // failure -- nothing added to list
0115: } catch (InterruptedException e) {
0116: // failure -- nothing added to list
0117: }
0118: if (browsers.size() == 0) {
0119: execLocal = null;
0120: } else {
0121: execLocal = browsers.toArray(new String[0]);
0122: }
0123: } else if (System.getProperty("os.name").startsWith("SunOS")) {
0124: execLocal = new String[] { "/usr/dt/bin/sdtwebclient {0}" };
0125: } else {
0126: ArrayList<String> browsers = new ArrayList<String>();
0127: try {
0128: Process p = Runtime.getRuntime().exec("which firefox");
0129: if (p.waitFor() == 0) {
0130: browsers.add("firefox -remote openURL({0})");
0131: browsers.add("firefox {0}");
0132: }
0133: } catch (IOException e) {
0134: // failure -- nothing added to list
0135: } catch (InterruptedException e) {
0136: // failure -- nothing added to list
0137: }
0138: try {
0139: Process p = Runtime.getRuntime().exec("which mozilla");
0140: if (p.waitFor() == 0) {
0141: browsers.add("mozilla -remote openURL({0})");
0142: browsers.add("mozilla {0}");
0143: }
0144: } catch (IOException e) {
0145: // failure -- nothing added to list
0146: } catch (InterruptedException e) {
0147: // failure -- nothing added to list
0148: }
0149: try {
0150: Process p = Runtime.getRuntime().exec("which opera");
0151: if (p.waitFor() == 0) {
0152: browsers.add("opera -remote openURL({0})");
0153: browsers.add("opera {0}");
0154: }
0155: } catch (IOException e) {
0156: // failure -- nothing added to list
0157: } catch (InterruptedException e) {
0158: // failure -- nothing added to list
0159: }
0160: try {
0161: Process p = Runtime.getRuntime().exec("which galeon");
0162: if (p.waitFor() == 0) {
0163: browsers.add("galeon {0}");
0164: }
0165: } catch (IOException e) {
0166: // failure -- nothing added to list
0167: } catch (InterruptedException e) {
0168: // failure -- nothing added to list
0169: }
0170: try {
0171: Process p = Runtime.getRuntime()
0172: .exec("which konqueror");
0173: if (p.waitFor() == 0) {
0174: browsers.add("konqueror {0}");
0175: }
0176: } catch (IOException e) {
0177: // failure -- nothing added to list
0178: } catch (InterruptedException e) {
0179: // failure -- nothing added to list
0180: }
0181: try {
0182: Process p = Runtime.getRuntime().exec("which netscape");
0183: if (p.waitFor() == 0) {
0184: browsers.add("netscape -remote openURL({0})");
0185: browsers.add("netscape {0}");
0186: }
0187: } catch (IOException e) {
0188: // failure -- nothing added to list
0189: } catch (InterruptedException e) {
0190: // failure -- nothing added to list
0191: }
0192: try {
0193: Process p = Runtime.getRuntime().exec("which xterm");
0194: if (p.waitFor() == 0) {
0195: p = Runtime.getRuntime().exec("which lynx");
0196: if (p.waitFor() == 0) {
0197: browsers.add("xterm -e lynx {0}");
0198: }
0199: }
0200: } catch (IOException e) {
0201: // failure -- nothing added to list
0202: } catch (InterruptedException e) {
0203: // failure -- nothing added to list
0204: }
0205: if (browsers.size() == 0) {
0206: execLocal = null;
0207: } else {
0208: execLocal = browsers.toArray(new String[0]);
0209: }
0210: }
0211: return execLocal;
0212: }
0213:
0214: /**
0215: * Save the options used to the given properties file.
0216: * Property names used will all start with com.Ostermiller.util.Browser
0217: * Properties are saved in such a way that a call to load(props); will
0218: * restore the state of this class.
0219: * If the default commands to open a browser are being used then
0220: * they are not saved in the properties file, assuming that the user
0221: * will want to use the defaults next time even if the defaults change.
0222: *
0223: * @param props properties file to which configuration is saved.
0224: *
0225: * @since ostermillerutils 1.00.00
0226: */
0227: public static void save(Properties props) {
0228: boolean saveBrowser = false;
0229: if (Browser.exec != null && Browser.exec.length > 0) {
0230: String[] execLocal = Browser.defaultCommands();
0231: if (execLocal != null
0232: && execLocal.length == Browser.exec.length) {
0233: for (int i = 0; i < execLocal.length; i++) {
0234: if (!execLocal[i].equals(Browser.exec[i])) {
0235: saveBrowser = true;
0236: }
0237: }
0238: } else {
0239: saveBrowser = true;
0240: }
0241: }
0242: if (saveBrowser) {
0243: StringBuffer sb = new StringBuffer();
0244: for (int i = 0; Browser.exec != null
0245: && i < Browser.exec.length; i++) {
0246: sb.append(Browser.exec[i]).append('\n');
0247: }
0248: props.put("com.Ostermiller.util.Browser.open", sb
0249: .toString());
0250: } else {
0251: props.remove("com.Ostermiller.util.Browser.open");
0252: }
0253: }
0254:
0255: /**
0256: * Load the options for this class from the given properties file.
0257: * This method is designed to work with the save(props) method. All
0258: * properties used will start with com.Ostermiller.util.Browser. If
0259: * no configuration is found, the default configuration will be used.
0260: * If this method is used, a call to Browser.init(); is not needed.
0261: *
0262: * @param props properties file from which configuration is loaded.
0263: *
0264: * @since ostermillerutils 1.00.00
0265: */
0266: public static void load(Properties props) {
0267: if (props.containsKey("com.Ostermiller.util.Browser.open")) {
0268: java.util.StringTokenizer tok = new java.util.StringTokenizer(
0269: props
0270: .getProperty("com.Ostermiller.util.Browser.open"),
0271: "\r\n", false);
0272: int count = tok.countTokens();
0273: String[] exec = new String[count];
0274: for (int i = 0; i < count; i++) {
0275: exec[i] = tok.nextToken();
0276: }
0277: Browser.exec = exec;
0278: } else {
0279: Browser.init();
0280: }
0281: }
0282:
0283: /**
0284: * Display a URL in the system browser.
0285: *
0286: * Browser.init() should be called before calling this function or
0287: * Browser.exec should be set explicitly.
0288: *
0289: * For security reasons, the URL will may not be passed directly to the
0290: * browser as it is passed to this method. The URL may be made safe for
0291: * the exec command by URLEncoding the URL before passing it.
0292: *
0293: * @param url the url to display
0294: * @throws IOException if the url is not valid or the browser fails to star
0295: *
0296: * @since ostermillerutils 1.00.00
0297: */
0298: public static void displayURL(String url) throws IOException {
0299: if (exec == null || exec.length == 0) {
0300: if (System.getProperty("os.name").startsWith("Mac")) {
0301: boolean success = false;
0302: try {
0303: Class<?> nSWorkspace;
0304: if (new File(
0305: "/System/Library/Java/com/apple/cocoa/application/NSWorkspace.class")
0306: .exists()) {
0307: // Mac OS X has NSWorkspace, but it is not in the classpath, add it.
0308: ClassLoader classLoader = new URLClassLoader(
0309: new URL[] { new File(
0310: "/System/Library/Java").toURL() });
0311: nSWorkspace = Class
0312: .forName(
0313: "com.apple.cocoa.application.NSWorkspace",
0314: true, classLoader);
0315: } else {
0316: nSWorkspace = Class
0317: .forName("com.apple.cocoa.application.NSWorkspace");
0318: }
0319: Method sharedWorkspace = nSWorkspace.getMethod(
0320: "sharedWorkspace", new Class[] {});
0321: Object workspace = sharedWorkspace.invoke(null,
0322: new Object[] {});
0323: Method openURL = nSWorkspace
0324: .getMethod("openURL", new Class[] { Class
0325: .forName("java.net.URL") });
0326: success = ((Boolean) openURL.invoke(workspace,
0327: new Object[] { new java.net.URL(url) }))
0328: .booleanValue();
0329: //success = com.apple.cocoa.application.NSWorkspace.sharedWorkspace().openURL(new java.net.URL(url));
0330: } catch (Exception x) {
0331: success = false;
0332: }
0333: if (!success) {
0334: try {
0335: Class<?> mrjFileUtils = Class
0336: .forName("com.apple.mrj.MRJFileUtils");
0337: Method openURL = mrjFileUtils.getMethod(
0338: "openURL", new Class[] { Class
0339: .forName("java.lang.String") });
0340: openURL.invoke(null, new Object[] { url });
0341: //com.apple.mrj.MRJFileUtils.openURL(url);
0342: } catch (Exception x) {
0343: System.err.println(x.getMessage());
0344: throw new IOException(labels
0345: .getString("failed"));
0346: }
0347: }
0348: } else {
0349: throw new IOException(labels.getString("nocommand"));
0350: }
0351: } else {
0352: // for security, see if the url is valid.
0353: // this is primarily to catch an attack in which the url
0354: // starts with a - to fool the command line flags, but
0355: // it could catch other stuff as well, and will throw a
0356: // MalformedURLException which will give the caller of this
0357: // function useful information.
0358: new URL(url);
0359: // escape any weird characters in the url. This is primarily
0360: // to prevent an attacker from putting in spaces
0361: // that might fool exec into allowing
0362: // the attacker to execute arbitrary code.
0363: StringBuffer sb = new StringBuffer(url.length());
0364: for (int i = 0; i < url.length(); i++) {
0365: char c = url.charAt(i);
0366: if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
0367: || (c >= '0' && c <= '9') || c == '.'
0368: || c == ':' || c == '&' || c == '@' || c == '/'
0369: || c == '?' || c == '%' || c == '+' || c == '='
0370: || c == '#' || c == '-' || c == '\\') {
0371: //characters that are necessary for URLs and should be safe
0372: //to pass to exec. Exec uses a default string tokenizer with
0373: //the default arguments (whitespace) to separate command line
0374: //arguments, so there should be no problem with anything but
0375: //whitespace.
0376: sb.append(c);
0377: } else {
0378: c = (char) (c & 0xFF); // get the lowest 8 bits (URLEncoding)
0379: if (c < 0x10) {
0380: sb.append("%0" + Integer.toHexString(c));
0381: } else {
0382: sb.append("%" + Integer.toHexString(c));
0383: }
0384: }
0385: }
0386: String[] messageArray = new String[1];
0387: messageArray[0] = sb.toString();
0388: String command = null;
0389: boolean found = false;
0390: // try each of the exec commands until something works
0391: try {
0392: for (int i = 0; i < exec.length && !found; i++) {
0393: try {
0394: // stick the url into the command
0395: command = MessageFormat.format(exec[i],
0396: (Object[]) messageArray);
0397: // parse the command line.
0398: ArrayList<String> argumentList = new ArrayList<String>();
0399: BrowserCommandLexer lex = new BrowserCommandLexer(
0400: new StringReader(command));
0401: String t;
0402: while ((t = lex.getNextToken()) != null) {
0403: argumentList.add(t);
0404: }
0405: String[] args = new String[argumentList.size()];
0406: args = argumentList.toArray(args);
0407: // the windows url protocol handler doesn't work well with file URLs.
0408: // Correct those problems here before continuing
0409: // Java File.toURL() gives only one / following file: but
0410: // we need two.
0411: // If there are escaped characters in the url, we will have
0412: // to create an Internet shortcut and open that, as the command
0413: // line version of the rundll doesn't like them.
0414: boolean useShortCut = false;
0415: if (args[0].equals("rundll32")
0416: && args[1]
0417: .equals("url.dll,FileProtocolHandler")) {
0418: if (args[2].startsWith("file:/")) {
0419: if (args[2].charAt(6) != '/') {
0420: args[2] = "file://"
0421: + args[2].substring(6);
0422: }
0423: if (args[2].charAt(7) != '/') {
0424: args[2] = "file:///"
0425: + args[2].substring(7);
0426: }
0427: useShortCut = true;
0428: } else if (args[2].toLowerCase().endsWith(
0429: "html")
0430: || args[2].toLowerCase().endsWith(
0431: "htm")) {
0432: useShortCut = true;
0433: }
0434: }
0435: if (useShortCut) {
0436: File shortcut = File.createTempFile(
0437: "OpenInBrowser", ".url");
0438: shortcut = shortcut.getCanonicalFile();
0439: shortcut.deleteOnExit();
0440: PrintWriter out = new PrintWriter(
0441: new FileWriter(shortcut));
0442: out.println("[InternetShortcut]");
0443: out.println("URL=" + args[2]);
0444: out.close();
0445: args[2] = shortcut.getCanonicalPath();
0446: }
0447: // start the browser
0448: Process p = Runtime.getRuntime().exec(args);
0449:
0450: // give the browser a bit of time to fail.
0451: // I have found that sometimes sleep doesn't work
0452: // the first time, so do it twice. My tests
0453: // seem to show that 1000 milliseconds is enough
0454: // time for the browsers I'm using.
0455: for (int j = 0; j < 2; j++) {
0456: try {
0457: Thread.sleep(1000);
0458: } catch (InterruptedException ix) {
0459: throw new RuntimeException(ix);
0460: }
0461: }
0462: if (p.exitValue() == 0) {
0463: // this is a weird case. The browser exited after
0464: // a couple seconds saying that it successfully
0465: // displayed the url. Either the browser is lying
0466: // or the user closed it *really* quickly. Oh well.
0467: found = true;
0468: }
0469: } catch (IOException x) {
0470: // the command was not a valid command.
0471: System.err.println(labels.getString("warning")
0472: + " " + x.getMessage());
0473: }
0474: }
0475: if (!found) {
0476: // we never found a command that didn't terminate with an error.
0477: throw new IOException(labels.getString("failed"));
0478: }
0479: } catch (IllegalThreadStateException e) {
0480: // the browser is still running. This is a good sign.
0481: // lets just say that it is displaying the url right now!
0482: }
0483: }
0484: }
0485:
0486: /**
0487: * Display the URLs, each in their own window, in the system browser.
0488: *
0489: * Browser.init() should be called before calling this function or
0490: * Browser.exec should be set explicitly.
0491: *
0492: * If more than one URL is given an HTML page containing JavaScript will
0493: * be written to the local drive, that page will be opened, and it will
0494: * open the rest of the URLs.
0495: *
0496: * @param urls the list of urls to display
0497: * @throws IOException if the url is not valid or the browser fails to star
0498: *
0499: * @since ostermillerutils 1.00.00
0500: */
0501: public static void displayURLs(String[] urls) throws IOException {
0502: if (urls == null || urls.length == 0) {
0503: return;
0504: }
0505: if (urls.length == 1) {
0506: displayURL(urls[0]);
0507: return;
0508: }
0509: File shortcut = File.createTempFile("DisplayURLs", ".html");
0510: shortcut = shortcut.getCanonicalFile();
0511: shortcut.deleteOnExit();
0512: PrintWriter out = new PrintWriter(new FileWriter(shortcut));
0513: out.println("<!-- saved from url=(0014)about:internet -->");
0514: out.println("<html>");
0515: out.println("<head>");
0516: out.println("<title>" + labels.getString("html.openurls")
0517: + "</title>");
0518: out
0519: .println("<script language=\"javascript\" type=\"text/javascript\">");
0520: out.println("function displayURLs(){");
0521: for (int i = 1; i < urls.length; i++) {
0522: out
0523: .println("window.open(\""
0524: + urls[i]
0525: + "\", \"_blank\", \"toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes\");");
0526: }
0527: out.println("location.href=\"" + urls[0] + "\";");
0528: out.println("}");
0529: out.println("</script>");
0530: out.println("</head>");
0531: out.println("<body onload=\"javascript:displayURLs()\">");
0532: out.println("<noscript>");
0533: for (String element : urls) {
0534: out.println("<a target=\"_blank\" href=\"" + element
0535: + "\">" + element + "</a><br>");
0536: }
0537: out.println("</noscript>");
0538: out.println("</body>");
0539: out.println("</html>");
0540: out.close();
0541: displayURL(shortcut.toURL().toString());
0542: }
0543:
0544: /**
0545: * Display the URL in a new window.
0546: *
0547: * Uses javascript to check history.length to determine if the browser opened a
0548: * new window already. If it did, the url is shown in that window, if not, it is
0549: * shown in new window.
0550: *
0551: * Some browsers do not allow the length of history to be viewed by a web page. In that
0552: * case, the url will be displayed in the current window.
0553: *
0554: * Browser.init() should be called before calling this function or
0555: * Browser.exec should be set explicitly.
0556: *
0557: * @param url the url to display in a new window.
0558: * @throws IOException if the url is not valid or the browser fails to star
0559: *
0560: * @since ostermillerutils 1.00.00
0561: */
0562: public static void displayURLinNew(String url) throws IOException {
0563: displayURLsinNew(new String[] { url });
0564: }
0565:
0566: /**
0567: * Display the URLs, each in their own window, in the system browser and the first in
0568: * the named window.
0569: *
0570: * The first URL will only be opened in the named window if the browser did no
0571: * open it in a new window to begin with.
0572: *
0573: * Browser.init() should be called before calling this function or
0574: * Browser.exec should be set explicitly.
0575: *
0576: * An html page containing javascript will
0577: * be written to the local drive, that page will be opened, and it will
0578: * open all the urls.
0579: *
0580: * @param urls the list of urls to display
0581: * @throws IOException if the url is not valid or the browser fails to star
0582: *
0583: * @since ostermillerutils 1.00.00
0584: */
0585: public static void displayURLsinNew(String[] urls)
0586: throws IOException {
0587: if (urls == null || urls.length == 0) {
0588: return;
0589: }
0590: File shortcut = File.createTempFile("DisplayURLs", ".html");
0591: shortcut.deleteOnExit();
0592: shortcut = shortcut.getCanonicalFile();
0593: PrintWriter out = new PrintWriter(new FileWriter(shortcut));
0594: out.println("<!-- saved from url=(0014)about:internet -->");
0595: out.println("<html>");
0596: out.println("<head>");
0597: out.println("<title>" + labels.getString("html.openurls")
0598: + "</title>");
0599: out
0600: .println("<script language=\"javascript\" type=\"text/javascript\">");
0601: out.println("function displayURLs(){");
0602: out.println("var hlength = 0;");
0603: out.println("try {");
0604: out.println("hlength = history.length;");
0605: out.println("} catch (e) {}");
0606: out.println("if (hlength>0) {");
0607: out
0608: .println("window.open(\""
0609: + urls[0]
0610: + "\", \"_blank\", \"toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes\");");
0611: out.println("}");
0612: for (int i = 1; i < urls.length; i++) {
0613: out
0614: .println("window.open(\""
0615: + urls[i]
0616: + "\", \"_blank\", \"toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes\");");
0617: }
0618: out.println("if (hlength==0) {");
0619: out.println("location.href=\"" + urls[0] + "\";");
0620: out.println("} else {");
0621: out.println("history.back()");
0622: out.println("}");
0623: out.println("}");
0624: out.println("</script>");
0625: out.println("</head>");
0626: out.println("<body onload=\"javascript:displayURLs()\">");
0627: out.println("<noscript>");
0628: for (String element : urls) {
0629: out.println("<a target=\"_blank\" href=\"" + element
0630: + "\">" + element + "</a><br>");
0631: }
0632: out.println("</noscript>");
0633: out.println("</body>");
0634: out.println("</html>");
0635: out.close();
0636: displayURL(shortcut.toURL().toString());
0637: }
0638:
0639: /**
0640: * Display the URL in the named window.
0641: *
0642: * If the browser opens a new window by default, this will likely cause a duplicate window
0643: * to be opened.
0644: *
0645: * Browser.init() should be called before calling this function or
0646: * Browser.exec should be set explicitly.
0647: *
0648: * @param url the url to display
0649: * @param namedWindow the name of the desired window.
0650: * @throws IOException if the url is not valid or the browser fails to star
0651: *
0652: * @since ostermillerutils 1.00.00
0653: */
0654: public static void displayURL(String url, String namedWindow)
0655: throws IOException {
0656: displayURLs(new String[] { url }, new String[] { namedWindow });
0657: }
0658:
0659: /**
0660: * Display the URLs in the named windows.
0661: *
0662: * If the browser opens a new window by default, this will likely cause a duplicate window
0663: * to be opened. This method relies on the browser to support javascript.
0664: *
0665: * Browser.init() should be called before calling this function or
0666: * Browser.exec should be set explicitly.
0667: *
0668: * Extra names for windows will be ignored, and if there are too few names, the remaining
0669: * windows will be named "_blank".
0670: *
0671: * @param urls the list of urls to display
0672: * @param namedWindows the list of names for the windows.
0673: * @throws IOException if the url is not valid or the browser fails to star
0674: *
0675: * @since ostermillerutils 1.00.00
0676: */
0677: public static void displayURLs(String[] urls, String[] namedWindows)
0678: throws IOException {
0679: if (urls == null || urls.length == 0) {
0680: return;
0681: }
0682: File shortcut = File.createTempFile("DisplayURLs", ".html");
0683: shortcut.deleteOnExit();
0684: shortcut = shortcut.getCanonicalFile();
0685: PrintWriter out = new PrintWriter(new FileWriter(shortcut));
0686: out.println("<!-- saved from url=(0014)about:internet -->");
0687: out.println("<html>");
0688: out.println("<head>");
0689: out.println("<title>" + labels.getString("html.openurls")
0690: + "</title>");
0691: out
0692: .println("<base target=\""
0693: + ((namedWindows == null
0694: || namedWindows.length == 0 || namedWindows[0] == null) ? "_blank"
0695: : namedWindows[0]) + "\">");
0696: out
0697: .println("<script language=\"javascript\" type=\"text/javascript\">");
0698: for (int i = 1; i < urls.length; i++) {
0699: out
0700: .println("window.open(\""
0701: + urls[i]
0702: + "\", \""
0703: + ((namedWindows == null
0704: || namedWindows.length <= i || namedWindows[i] == null) ? "_blank"
0705: : namedWindows[i])
0706: + "\", \"toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes\");");
0707: }
0708: out.println("location.href=\"" + urls[0] + "\";");
0709: out.println("</script>");
0710: out.println("</head>");
0711: out.println("<body onload=\"javascript:displayURLs()\">");
0712: out.println("<noscript>");
0713: for (String element : urls) {
0714: out
0715: .println("<a target=\""
0716: + ((namedWindows == null
0717: || namedWindows.length == 0 || namedWindows[0] == null) ? "_blank"
0718: : namedWindows[0]) + "\" href=\""
0719: + element + "\">" + element + "</a><br>");
0720: }
0721: out.println("</noscript>");
0722: out.println("</body>");
0723: out.println("</html>");
0724: out.close();
0725: displayURL(shortcut.toURL().toString());
0726: }
0727:
0728: /**
0729: * Display the URLs the first in the given named window.
0730: *
0731: * If the browser opens a new window by default, this will likely cause a duplicate window
0732: * to be opened. This method relies on the browser to support javascript.
0733: *
0734: * Browser.init() should be called before calling this function or
0735: * Browser.exec should be set explicitly.
0736: *
0737: * @param urls the list of urls to display
0738: * @param namedWindow the name of the first window to use.
0739: * @throws IOException if the url is not valid or the browser fails to star
0740: *
0741: * @since ostermillerutils 1.00.00
0742: */
0743: public static void displayURLs(String[] urls, String namedWindow)
0744: throws IOException {
0745: displayURLs(urls, new String[] { namedWindow });
0746: }
0747:
0748: /**
0749: * Open the url(s) specified on the command line in your browser.
0750: *
0751: * @param args Command line arguments (URLs)
0752: */
0753: public static void main(String[] args) {
0754: try {
0755: Browser.init();
0756: if (Browser.dialogConfiguration(null)) {
0757: if (args.length == 0) {
0758: Browser.displayURLs(new String[] {
0759: "http://www.google.com/",
0760: "http://dmoz.org/",
0761: "http://ostermiller.org", }, "fun");
0762: } else if (args.length == 1) {
0763: Browser.displayURL(args[0], "fun");
0764: } else {
0765: Browser.displayURLs(args, "fun");
0766: }
0767: }
0768: try {
0769: Thread.sleep(10000);
0770: } catch (InterruptedException ix) {
0771: throw new RuntimeException(ix);
0772: }
0773: } catch (IOException e) {
0774: System.err.println(e.getMessage());
0775: }
0776: System.exit(0);
0777: }
0778:
0779: /**
0780: * Show a dialog that allows the user to configure the
0781: * command lines used for starting a browser on their system.
0782: *
0783: * @param owner The frame that owns the dialog.
0784: * @return whether or not the dialog has changed
0785: *
0786: * @since ostermillerutils 1.00.00
0787: */
0788: public static boolean dialogConfiguration(Frame owner) {
0789: dialogConfiguration(owner, null);
0790: return Browser.dialog.changed();
0791: }
0792:
0793: /**
0794: * Show a dialog that allows the user to configure the
0795: * command lines used for starting a browser on their system.
0796: * String used in the dialog are taken from the given
0797: * properties. This dialog can be customized or displayed in
0798: * multiple languages.
0799: * <P>
0800: * Properties that are used:
0801: * com.Ostermiller.util.BrowserDialog.title<br>
0802: * com.Ostermiller.util.BrowserDialog.description<br>
0803: * com.Ostermiller.util.BrowserDialog.label<br>
0804: * com.Ostermiller.util.BrowserDialog.defaults<br>
0805: * com.Ostermiller.util.BrowserDialog.browse<br>
0806: * com.Ostermiller.util.BrowserDialog.ok<br>
0807: * com.Ostermiller.util.BrowserDialog.cancel<br>
0808: *
0809: * @param owner The frame that owns this dialog.
0810: * @param props contains the strings used in the dialog.
0811: * @return whether or not the dialog has changed
0812: * @deprecated Use the com.Ostermiller.util.Browser resource bundle to set strings for the given locale.
0813: *
0814: * @since ostermillerutils 1.00.00
0815: */
0816: @Deprecated
0817: public static boolean dialogConfiguration(Frame owner,
0818: Properties props) {
0819: if (Browser.dialog == null) {
0820: Browser.dialog = new BrowserDialog(owner);
0821: }
0822: if (props != null) {
0823: Browser.dialog.setProps(props);
0824: }
0825: Browser.dialog.show();
0826: return Browser.dialog.changed();
0827: }
0828:
0829: /**
0830: * Where the command lines are typed.
0831: *
0832: * @since ostermillerutils 1.00.00
0833: */
0834: private static JTextArea description;
0835:
0836: /**
0837: * Where the command lines are typed.
0838: *
0839: * @since ostermillerutils 1.00.00
0840: */
0841: private static JTextArea commandLinesArea;
0842:
0843: /**
0844: * The reset button.
0845: *
0846: * @since ostermillerutils 1.00.00
0847: */
0848: private static JButton resetButton;
0849:
0850: /**
0851: * The browse button.
0852: *
0853: * @since ostermillerutils 1.00.00
0854: */
0855: private static JButton browseButton;
0856:
0857: /**
0858: * The label for the field in which the name is typed.
0859: *
0860: * @since ostermillerutils 1.00.00
0861: */
0862: private static JLabel commandLinesLabel;
0863:
0864: /**
0865: * File dialog for choosing a browser
0866: *
0867: * @since ostermillerutils 1.00.00
0868: */
0869: private static JFileChooser fileChooser;
0870:
0871: /**
0872: * A panel used in the options dialog. Null until getDialogPanel() is called.
0873: */
0874: private static JPanel dialogPanel = null;
0875: private static Window dialogParent = null;
0876:
0877: /**
0878: * If you wish to add to your own dialog box rather than have a separate
0879: * one just for the browser, use this method to get a JPanel that can
0880: * be added to your own dialog.
0881: *
0882: * mydialog.add(Browser.getDialogPanel(mydialog));
0883: * Browser.initPanel();
0884: * mydialog.show();
0885: * if (ok_pressed){
0886: * Browser.userOKedPanelChanges();
0887: * }
0888: *
0889: * @param parent window into which panel with eventually be placed.
0890: * @return the dialog
0891: * @since ostermillerutils 1.02.22
0892: */
0893: public static JPanel getDialogPanel(Window parent) {
0894: dialogParent = parent;
0895: if (dialogPanel == null) {
0896: commandLinesArea = new JTextArea("", 8, 40);
0897: JScrollPane scrollpane = new JScrollPane(commandLinesArea);
0898: resetButton = new JButton(labels.getString("dialog.reset"));
0899: browseButton = new JButton(labels
0900: .getString("dialog.browse"));
0901: commandLinesLabel = new JLabel(labels
0902: .getString("dialog.commandLines"));
0903: description = new JTextArea(labels
0904: .getString("dialog.description"));
0905: description.setEditable(false);
0906: description.setOpaque(false);
0907:
0908: ActionListener actionListener = new ActionListener() {
0909: public void actionPerformed(ActionEvent e) {
0910: Object source = e.getSource();
0911: if (source == resetButton) {
0912: setCommands(Browser.defaultCommands());
0913: } else if (source == browseButton) {
0914: if (fileChooser == null) {
0915: fileChooser = new JFileChooser();
0916: }
0917: if (fileChooser.showOpenDialog(dialogParent) == JFileChooser.APPROVE_OPTION) {
0918: String app = fileChooser.getSelectedFile()
0919: .getPath();
0920: StringBuffer sb = new StringBuffer(2 * app
0921: .length());
0922: for (int i = 0; i < app.length(); i++) {
0923: char c = app.charAt(i);
0924: // escape these two characters so that we can later parse the stuff
0925: if (c == '\"' || c == '\\') {
0926: sb.append('\\');
0927: }
0928: sb.append(c);
0929: }
0930: app = sb.toString();
0931: if (app.indexOf(" ") != -1) {
0932: app = '"' + app + '"';
0933: }
0934: String commands = commandLinesArea
0935: .getText();
0936: if (commands.length() != 0
0937: && !commands.endsWith("\n")
0938: && !commands.endsWith("\r")) {
0939: commands += "\n";
0940: }
0941: commandLinesArea.setText(commands + app
0942: + " {0}");
0943: }
0944: }
0945: }
0946: };
0947:
0948: GridBagLayout gridbag = new GridBagLayout();
0949: GridBagConstraints c = new GridBagConstraints();
0950: c.insets.top = 5;
0951: c.insets.bottom = 5;
0952: dialogPanel = new JPanel(gridbag);
0953: dialogPanel.setBorder(BorderFactory.createEmptyBorder(10,
0954: 20, 5, 20));
0955:
0956: c.gridwidth = GridBagConstraints.REMAINDER;
0957: c.anchor = GridBagConstraints.WEST;
0958: gridbag.setConstraints(description, c);
0959: dialogPanel.add(description);
0960:
0961: c.gridy = 1;
0962: c.gridwidth = GridBagConstraints.RELATIVE;
0963: gridbag.setConstraints(commandLinesLabel, c);
0964: dialogPanel.add(commandLinesLabel);
0965: JPanel buttonPanel = new JPanel();
0966: c.anchor = GridBagConstraints.EAST;
0967: browseButton.addActionListener(actionListener);
0968: buttonPanel.add(browseButton);
0969: resetButton.addActionListener(actionListener);
0970: buttonPanel.add(resetButton);
0971: gridbag.setConstraints(buttonPanel, c);
0972: dialogPanel.add(buttonPanel);
0973:
0974: c.gridy = 2;
0975: c.gridwidth = GridBagConstraints.REMAINDER;
0976: c.anchor = GridBagConstraints.WEST;
0977: gridbag.setConstraints(scrollpane, c);
0978: dialogPanel.add(scrollpane);
0979: }
0980: return dialogPanel;
0981: }
0982:
0983: /**
0984: * A modal dialog that presents configuration option for this class.
0985: *
0986: * @since ostermillerutils 1.00.00
0987: */
0988: private static class BrowserDialog extends JDialog {
0989:
0990: /**
0991: * Serial version ID
0992: */
0993: private static final long serialVersionUID = -6021583796243635266L;
0994:
0995: /**
0996: * The OK button.
0997: *
0998: * @since ostermillerutils 1.00.00
0999: */
1000: private JButton okButton;
1001:
1002: /**
1003: * The cancel button.
1004: *
1005: * @since ostermillerutils 1.00.00
1006: */
1007: private JButton cancelButton;
1008:
1009: /**
1010: * The label for the field in which the name is typed.
1011: *
1012: * @since ostermillerutils 1.00.00
1013: */
1014: private JLabel commandLinesLabel;
1015:
1016: /**
1017: * update this variable when the user makes an action
1018: *
1019: * @since ostermillerutils 1.00.00
1020: */
1021: private boolean pressed_OK = false;
1022:
1023: /**
1024: * Properties that are used:
1025: * com.Ostermiller.util.BrowserDialog.title<br>
1026: * com.Ostermiller.util.BrowserDialog.description<br>
1027: * com.Ostermiller.util.BrowserDialog.label<br>
1028: * com.Ostermiller.util.BrowserDialog.defaults<br>
1029: * com.Ostermiller.util.BrowserDialog.browse<br>
1030: * com.Ostermiller.util.BrowserDialog.ok<br>
1031: * com.Ostermiller.util.BrowserDialog.cancel<br>
1032: *
1033: * @deprecated Use the com.Ostermiller.util.Browser resource bundle to set strings for the given locale.
1034: * @since ostermillerutils 1.00.00
1035: */
1036: @Deprecated
1037: private void setProps(Properties props) {
1038: if (props
1039: .containsKey("com.Ostermiller.util.BrowserDialog.title")) {
1040: setTitle(props
1041: .getProperty("com.Ostermiller.util.BrowserDialog.title"));
1042: }
1043: if (props
1044: .containsKey("com.Ostermiller.util.BrowserDialog.description")) {
1045: description
1046: .setText(props
1047: .getProperty("com.Ostermiller.util.BrowserDialog.description"));
1048: }
1049: if (props
1050: .containsKey("com.Ostermiller.util.BrowserDialog.label")) {
1051: commandLinesLabel
1052: .setText(props
1053: .getProperty("com.Ostermiller.util.BrowserDialog.label"));
1054: }
1055: if (props
1056: .containsKey("com.Ostermiller.util.BrowserDialog.defaults")) {
1057: resetButton
1058: .setText(props
1059: .getProperty("com.Ostermiller.util.BrowserDialog.defaults"));
1060: }
1061: if (props
1062: .containsKey("com.Ostermiller.util.BrowserDialog.browse")) {
1063: browseButton
1064: .setText(props
1065: .getProperty("com.Ostermiller.util.BrowserDialog.browse"));
1066: }
1067: if (props
1068: .containsKey("com.Ostermiller.util.BrowserDialog.ok")) {
1069: okButton
1070: .setText(props
1071: .getProperty("com.Ostermiller.util.BrowserDialog.ok"));
1072: }
1073: if (props
1074: .containsKey("com.Ostermiller.util.BrowserDialog.cancel")) {
1075: cancelButton
1076: .setText(props
1077: .getProperty("com.Ostermiller.util.BrowserDialog.cancel"));
1078: }
1079: pack();
1080: }
1081:
1082: /**
1083: * Whether the user pressed the applied changes.
1084: * true if OK was pressed or the user otherwise applied new changes,
1085: * false if cancel was pressed or dialog was closed with no changes.
1086: * If called before the dialog is displayed and closed, the results
1087: * are not defined.
1088: *
1089: * @return if the user made changes to the browser configuration.
1090: *
1091: * @since ostermillerutils 1.00.00
1092: */
1093: public boolean changed() {
1094: return pressed_OK;
1095: }
1096:
1097: /**
1098: * Create this dialog with the given parent and title.
1099: *
1100: * @param parent window from which this dialog is launched
1101: * @since ostermillerutils 1.00.00
1102: */
1103: public BrowserDialog(Frame parent) {
1104: super (parent, labels.getString("dialog.title"), true);
1105: setLocationRelativeTo(parent);
1106: // super calls dialogInit, so we don't need to do it again.
1107: }
1108:
1109: /**
1110: * Called by constructors to initialize the dialog.
1111: *
1112: * @since ostermillerutils 1.00.00
1113: */
1114: @Override
1115: protected void dialogInit() {
1116:
1117: super .dialogInit();
1118:
1119: getContentPane().setLayout(new BorderLayout());
1120:
1121: getContentPane().add(getDialogPanel(this ),
1122: BorderLayout.CENTER);
1123:
1124: JPanel panel = new JPanel(new FlowLayout());
1125: okButton = new JButton(labels.getString("dialog.ok"));
1126: okButton.addActionListener(new ActionListener() {
1127: public void actionPerformed(ActionEvent e) {
1128: pressed_OK = true;
1129: BrowserDialog.this .setVisible(false);
1130: }
1131: });
1132: panel.add(okButton);
1133: cancelButton = new JButton(labels
1134: .getString("dialog.cancel"));
1135: cancelButton.addActionListener(new ActionListener() {
1136: public void actionPerformed(ActionEvent e) {
1137: pressed_OK = false;
1138: BrowserDialog.this .setVisible(false);
1139: }
1140: });
1141: panel.add(cancelButton);
1142:
1143: getContentPane().add(panel, BorderLayout.SOUTH);
1144:
1145: pack();
1146: }
1147:
1148: /**
1149: * Shows the dialog.
1150: *
1151: * @since ostermillerutils 1.00.00
1152: * @deprecated use setVisible(true);
1153: */
1154: @Override
1155: @Deprecated
1156: public void show() {
1157: setVisible(true);
1158: }
1159:
1160: /**
1161: * @see java.awt.Component#setVisible(boolean)
1162: */
1163: @Override
1164: public void setVisible(boolean visible) {
1165: if (visible) {
1166: initPanel();
1167: super .setVisible(true);
1168: if (pressed_OK) {
1169: userOKedPanelChanges();
1170: }
1171: } else {
1172: super .setVisible(false);
1173: }
1174: }
1175:
1176: }
1177:
1178: private static void setCommands(String[] newExec) {
1179: StringBuffer sb = new StringBuffer();
1180: for (int i = 0; newExec != null && i < newExec.length; i++) {
1181: sb.append(newExec[i]).append('\n');
1182: }
1183: commandLinesArea.setText(sb.toString());
1184: }
1185:
1186: /**
1187: * If you are using the getDialogPanel() method to create your own dialog, this
1188: * method should be called every time before you display the dialog.
1189: *
1190: * mydialog.add(Browser.getDialogPanel(mydialog));
1191: * Browser.initPanel();
1192: * mydialog.show();
1193: * if (ok_pressed){
1194: * Browser.userOKedPanelChanges();
1195: * }
1196: *
1197: * @since ostermillerutils 1.02.22
1198: */
1199: public static void initPanel() {
1200: setCommands(exec);
1201: }
1202:
1203: /**
1204: * If you are using the getDialogPanel() method to create your own dialog, this
1205: * method should be called after you display the dialog if the user pressed ok.
1206: *
1207: * mydialog.add(Browser.getDialogPanel(mydialog));
1208: * Browser.initPanel();
1209: * mydialog.show();
1210: * if (ok_pressed){
1211: * Browser.userOKedPanelChanges();
1212: * }
1213: *
1214: * @since ostermillerutils 1.02.22
1215: */
1216: public static void userOKedPanelChanges() {
1217: java.util.StringTokenizer tok = new java.util.StringTokenizer(
1218: commandLinesArea.getText(), "\r\n", false);
1219: int count = tok.countTokens();
1220: String[] exec = new String[count];
1221: for (int i = 0; i < count; i++) {
1222: exec[i] = tok.nextToken();
1223: }
1224: Browser.exec = exec;
1225: }
1226: }
|