diff --git a/src/dorkbox/util/Desktop.java b/src/dorkbox/util/Desktop.java new file mode 100644 index 0000000..f2d0f61 --- /dev/null +++ b/src/dorkbox/util/Desktop.java @@ -0,0 +1,146 @@ +/* + * Copyright 2017 dorkbox, llc + * + * Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC + * Derivative code has been released as Apache 2.0, used with permission. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import dorkbox.util.process.ShellExecutor; + +@SuppressWarnings("WeakerAccess") +public +class Desktop { + /** + * Launches the default browser to display a {@code URI}. + * + * If the default browser is not able to handle the specified {@code URI}, the application registered for handling + * {@code URIs} of the specified type is invoked. The application is determined from the protocol and path of the {@code URI}, as + * defined by the {@code URI} class. + * + * @param uri the URL to browse/open + * + * @throws IOException + */ + public static void browseURL(URI uri) throws IOException { + // Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT) + if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded && OSUtil.DesktopEnv.isGtk3) { + if (!ShellExecutor.run("xdg-open", uri.toString())) { + throw new IOException("Error running xdg-open for " + uri.toString()); + } + } + else { + if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.BROWSE)) { + java.awt.Desktop.getDesktop().browse(uri); + } else { + throw new IOException("Current OS and desktop configuration does not support browsing for a URL"); + } + } + } + + /** + * Launches the mail composing window of the user default mail client for the specified address. + * + * @param address who the email goes to + * + * @throws IOException + */ + public static void launchEmail(String address) throws IOException { + URI uri = null; + try { + uri = new URI(address); + launchEmail(uri); + } catch (URISyntaxException e) { + throw new IOException("Invalid URI " + address); + } + } + + /** + * Launches the mail composing window of the user default mail client, filling the message fields specified by a {@code mailto:} URI. + *

+ * A mailto: URI can specify message fields including "to", "cc", "subject", "body", etc. + * See The mailto URL scheme (RFC 2368) for the {@code mailto:} URI specification + * details. + * + * @param uri the specified {@code mailto:} URI + * + * @throws IOException + */ + public static void launchEmail(final URI uri) throws IOException { + // Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT) + if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded && OSUtil.DesktopEnv.isGtk3) { + if (!ShellExecutor.run("xdg-email", uri.toString())) { + throw new IOException("Error running xdg-email for " + uri.toString()); + } + } + else { + if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.MAIL)) { + java.awt.Desktop.getDesktop().mail(uri); + } else { + throw new IOException("Current OS and desktop configuration does not support launching an email client"); + } + } + } + + /** + * Opens the specified path in the system-default file browser. + * + * Works around several OS limitations: + * - Apple tries to launch .app bundle directories as applications rather than browsing contents + * - Linux has mixed support for Desktop.getDesktop(). Uses the xdg-open fallback. + * + * @param path The directory to browse + * + * @throws IOException + */ + public static void browseDirectory(String path) throws IOException { + if (OS.isMacOsX()) { + File directory = new File(path); + + // Mac tries to open the .app rather than browsing it. Instead, pass a child with -R to select it in finder + File[] files = directory.listFiles(); + if (files != null && files.length > 0) { + // Get first child + File child = files[0]; + if (!ShellExecutor.run("open", "-R", child.getCanonicalPath())) { + throw new IOException("Error opening the directory for " + path); + } + } + } else { + // Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT) + if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded && OSUtil.DesktopEnv.isGtk3) { + if (!ShellExecutor.run("xdg-open", path)) { + throw new IOException("Error running xdg-open for " + path); + } + } + else { + if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.OPEN)) { + java.awt.Desktop.getDesktop().open(new File(path)); + return; + } else { + throw new IOException("Current OS and desktop configuration does not support opening a directory to browse"); + } + } + } + + throw new IOException("Unable to open " + path); + } +} diff --git a/src/dorkbox/util/Framework.java b/src/dorkbox/util/Framework.java deleted file mode 100644 index 28b69ec..0000000 --- a/src/dorkbox/util/Framework.java +++ /dev/null @@ -1,123 +0,0 @@ -package dorkbox.util; - -import java.awt.Component; -import java.awt.Desktop; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; - -import javax.swing.JOptionPane; - -import org.slf4j.LoggerFactory; - -/** - * This class defines a mix of useful methods for different frameworks such as JavaFX or SWT. Swing, being that it is builtin, and always - * available to the JRE (JavaFX can be excluded...) is in it's own class ({@link Swing}). - */ -public -class Framework { - - public final static boolean isJavaFxLoaded; - public final static boolean isJavaFxGtk3; - - public final static boolean isSwtLoaded; - public final static boolean isSwtGtk3; - - - static { - boolean isJavaFxLoaded_ = false; - boolean isJavaFxGtk3_ = false; - - boolean isSwtLoaded_ = false; - boolean isSwtGtk3_ = false; - - try { - // this is important to use reflection, because if JavaFX is not being used, calling getToolkit() will initialize it... - java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); - m.setAccessible(true); - ClassLoader cl = ClassLoader.getSystemClassLoader(); - - // JavaFX Java7,8 is GTK2 only. Java9 can have it be GTK3 if -Djdk.gtk.version=3 is specified - // see http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html - isJavaFxLoaded_ = (null != m.invoke(cl, "com.sun.javafx.tk.Toolkit")) || (null != m.invoke(cl, "javafx.application.Application")); - - if (isJavaFxLoaded_) { - // JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified - // see - // http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html - // https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm - // from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+. - - isJavaFxGtk3_ = OS.javaVersion >= 9 && System.getProperty("jdk.gtk.version", "2").equals("3"); - } - - // maybe we should load the SWT version? (In order for us to work with SWT, BOTH must be the same!! - // SWT is GTK2, but if -DSWT_GTK3=1 is specified, it can be GTK3 - isSwtLoaded_ = null != m.invoke(cl, "org.eclipse.swt.widgets.Display"); - - if (isSwtLoaded_) { - // Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to - // System.setProperty("SWT_GTK3", "0"); (or -DSWT_GTK3=1) - - // was SWT forced? - String swt_gtk3 = System.getProperty("SWT_GTK3"); - isSwtGtk3_ = swt_gtk3 != null && !swt_gtk3.equals("0"); - if (!isSwtGtk3_) { - // check a different property - String property = System.getProperty("org.eclipse.swt.internal.gtk.version"); - isSwtGtk3_ = property != null && !property.startsWith("2."); - } - } - } catch (Throwable e) { - LoggerFactory.getLogger(Framework.class).debug("Error detecting if JavaFX/SWT is loaded", e); - } - - isJavaFxLoaded = isJavaFxLoaded_; - isJavaFxGtk3 = isJavaFxGtk3_; - - isSwtLoaded = isSwtLoaded_; - isSwtGtk3 = isSwtGtk3_; - } - - public static - void initDispatch() { - if (Framework.isJavaFxLoaded) { - // This will initialize javaFX dispatch methods - JavaFX.init(); - } - else if (Framework.isSwtLoaded) { - // This will initialize swt dispatch methods - SwtBytecodeOverride.init(); // necessary to properly fix methods in {@link Swt} - dorkbox.util.Swt.init(); - } - } - - - /** - * Opens the given website in the default browser, or show a message saying that no default browser could be accessed. - * - * @param parent The parent of the error message, if raised - * @param uri The website uri - */ - public static - void browse(final Component parent, final String uri) { - boolean cannotBrowse = false; - if (Desktop.isDesktopSupported() && Desktop.getDesktop() - .isSupported(Desktop.Action.BROWSE)) { - try { - Desktop.getDesktop() - .browse(new URI(uri)); - } catch (URISyntaxException ignored) { - } catch (IOException ex) { - cannotBrowse = true; - } - } - else { - cannotBrowse = true; - } - - if (cannotBrowse) { - JOptionPane.showMessageDialog(parent, "It seems that I can't open a website using your default browser, sorry."); - } - } -}