1 /*
   2  * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt;
  27 
  28 import java.awt.desktop.AboutHandler;
  29 import java.awt.desktop.OpenFilesHandler;
  30 import java.awt.desktop.OpenURIHandler;
  31 import java.awt.desktop.PreferencesHandler;
  32 import java.awt.desktop.PrintFilesHandler;
  33 import java.awt.desktop.QuitHandler;
  34 import java.awt.desktop.QuitStrategy;
  35 import java.awt.desktop.SystemEventListener;
  36 import java.awt.peer.DesktopPeer;
  37 import java.io.File;
  38 import java.io.FilePermission;
  39 import java.io.IOException;
  40 import java.net.MalformedURLException;
  41 import java.net.URI;
  42 import java.net.URISyntaxException;
  43 import java.net.URL;
  44 
  45 import sun.awt.SunToolkit;
  46 import javax.swing.JMenuBar;
  47 import sun.security.util.SecurityConstants;
  48 
  49 /**
  50  * The {@code Desktop} class allows interact with various desktop capabilities.
  51  *
  52  * <p> Supported operations include:
  53  * <ul>
  54  *   <li>launching the user-default browser to show a specified
  55  *       URI;</li>
  56  *   <li>launching the user-default mail client with an optional
  57  *       {@code mailto} URI;</li>
  58  *   <li>launching a registered application to open, edit or print a
  59  *       specified file.</li>
  60  * </ul>
  61  *
  62  * <p> This class provides methods corresponding to these
  63  * operations. The methods look for the associated application
  64  * registered on the current platform, and launch it to handle a URI
  65  * or file. If there is no associated application or the associated
  66  * application fails to be launched, an exception is thrown.
  67  *
  68  * Please see {@link Desktop.Action} for the full list of supported operations
  69  * and capabilities.
  70  *
  71  * <p> An application is registered to a URI or file type.
  72  * The mechanism of registering, accessing, and
  73  * launching the associated application is platform-dependent.
  74  *
  75  * <p> Each operation is an action type represented by the {@link
  76  * Desktop.Action} class.
  77  *
  78  * <p> Note: when some action is invoked and the associated
  79  * application is executed, it will be executed on the same system as
  80  * the one on which the Java application was launched.
  81  *
  82  * @see Action
  83  *
  84  * @since 1.6
  85  * @author Armin Chen
  86  * @author George Zhang
  87  */
  88 public class Desktop {
  89 
  90     /**
  91      * Represents an action type.  Each platform supports a different
  92      * set of actions.  You may use the {@link Desktop#isSupported}
  93      * method to determine if the given action is supported by the
  94      * current platform.
  95      * @see java.awt.Desktop#isSupported(java.awt.Desktop.Action)
  96      * @since 1.6
  97      */
  98     public static enum Action {
  99         /**
 100          * Represents an "open" action.
 101          * @see Desktop#open(java.io.File)
 102          */
 103         OPEN,
 104         /**
 105          * Represents an "edit" action.
 106          * @see Desktop#edit(java.io.File)
 107          */
 108         EDIT,
 109         /**
 110          * Represents a "print" action.
 111          * @see Desktop#print(java.io.File)
 112          */
 113         PRINT,
 114         /**
 115          * Represents a "mail" action.
 116          * @see Desktop#mail()
 117          * @see Desktop#mail(java.net.URI)
 118          */
 119         MAIL,
 120 
 121         /**
 122          * Represents a "browse" action.
 123          * @see Desktop#browse(java.net.URI)
 124          */
 125         BROWSE,
 126 
 127         /**
 128          * Represents an AppForegroundListener
 129          * @see java.awt.desktop.AppForegroundListener
 130          * @since 9
 131          */
 132         APP_EVENT_FOREGROUND,
 133 
 134         /**
 135          * Represents an AppHiddenListener
 136          * @see java.awt.desktop.AppHiddenListener
 137          * @since 9
 138          */
 139         APP_EVENT_HIDDEN,
 140 
 141         /**
 142          * Represents an AppReopenedListener
 143          * @see java.awt.desktop.AppReopenedListener
 144          * @since 9
 145          */
 146         APP_EVENT_REOPENED,
 147 
 148         /**
 149          * Represents a ScreenSleepListener
 150          * @see java.awt.desktop.ScreenSleepListener
 151          * @since 9
 152          */
 153         APP_EVENT_SCREEN_SLEEP,
 154 
 155         /**
 156          * Represents a SystemSleepListener
 157          * @see java.awt.desktop.SystemSleepListener
 158          * @since 9
 159          */
 160         APP_EVENT_SYSTEM_SLEEP,
 161 
 162         /**
 163          * Represents a UserSessionListener
 164          * @see java.awt.desktop.UserSessionListener
 165          * @since 9
 166          */
 167         APP_EVENT_USER_SESSION,
 168 
 169         /**
 170          * Represents an AboutHandler
 171          * @see #setAboutHandler(java.awt.desktop.AboutHandler)
 172          * @since 9
 173          */
 174         APP_ABOUT,
 175 
 176         /**
 177          * Represents a PreferencesHandler
 178          * @see #setPreferencesHandler(java.awt.desktop.PreferencesHandler)
 179          * @since 9
 180          */
 181         APP_PREFERENCES,
 182 
 183         /**
 184          * Represents an OpenFilesHandler
 185          * @see #setOpenFileHandler(java.awt.desktop.OpenFilesHandler)
 186          * @since 9
 187          */
 188         APP_OPEN_FILE,
 189 
 190         /**
 191          * Represents a PrintFilesHandler
 192          * @see #setPrintFileHandler(java.awt.desktop.PrintFilesHandler)
 193          * @since 9
 194          */
 195         APP_PRINT_FILE,
 196 
 197         /**
 198          * Represents an OpenURIHandler
 199          * @see #setOpenURIHandler(java.awt.desktop.OpenURIHandler)
 200          * @since 9
 201          */
 202         APP_OPEN_URI,
 203 
 204         /**
 205          * Represents a QuitHandler
 206          * @see #setQuitHandler(java.awt.desktop.QuitHandler)
 207          * @since 9
 208          */
 209         APP_QUIT_HANDLER,
 210 
 211         /**
 212          * Represents a QuitStrategy
 213          * @see #setQuitStrategy(java.awt.desktop.QuitStrategy)
 214          * @since 9
 215          */
 216         APP_QUIT_STRATEGY,
 217 
 218         /**
 219          * Represents a SuddenTermination
 220          * @see #enableSuddenTermination()
 221          * @since 9
 222          */
 223         APP_SUDDEN_TERMINATION,
 224 
 225         /**
 226          * Represents a requestForeground
 227          * @see #requestForeground(boolean)
 228          * @since 9
 229          */
 230         APP_REQUEST_FOREGROUND,
 231 
 232         /**
 233          * Represents a HelpViewer
 234          * @see #openHelpViewer()
 235          * @since 9
 236          */
 237         APP_HELP_VIEWER,
 238 
 239         /**
 240          * Represents a menu bar
 241          * @see #setDefaultMenuBar(javax.swing.JMenuBar)
 242          * @since 9
 243          */
 244         APP_MENU_BAR,
 245 
 246         /**
 247          * Represents a browse file directory
 248          * @see #browseFileDirectory(java.io.File)
 249          * @since 9
 250          */
 251         BROWSE_FILE_DIR,
 252 
 253         /**
 254          * Represents a move to trash
 255          * @see #moveToTrash(java.io.File)
 256          * @since 9
 257          */
 258         MOVE_TO_TRASH
 259     };
 260 
 261     private DesktopPeer peer;
 262 
 263     /**
 264      * Suppresses default constructor for noninstantiability.
 265      */
 266     private Desktop() {
 267         Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
 268         // same cast as in isDesktopSupported()
 269         if (defaultToolkit instanceof SunToolkit) {
 270             peer = ((SunToolkit) defaultToolkit).createDesktopPeer(this);
 271         }
 272     }
 273 
 274     private void checkEventsProcessingPermission() {
 275         SecurityManager sm = System.getSecurityManager();
 276         if (sm != null) {
 277             sm.checkPermission(new RuntimePermission(
 278                     "canProcessApplicationEvents"));
 279         }
 280     }
 281      
 282     private static Desktop desktop;
 283 
 284     /**
 285      * Returns the {@code Desktop} instance of the current
 286      * desktop context. On some platforms the Desktop API may not be
 287      * supported; use the {@link #isDesktopSupported} method to
 288      * determine if the current desktop is supported.
 289      * @return the Desktop instance
 290      * @throws HeadlessException if {@link
 291      * GraphicsEnvironment#isHeadless()} returns {@code true}
 292      * @throws UnsupportedOperationException if this class is not
 293      * supported on the current platform
 294      * @see #isDesktopSupported()
 295      * @see java.awt.GraphicsEnvironment#isHeadless
 296      */
 297     public static synchronized Desktop getDesktop(){
 298         if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
 299         if (!Desktop.isDesktopSupported()) {
 300             throw new UnsupportedOperationException("Desktop API is not " +
 301                                                     "supported on the current platform");
 302         }
 303 
 304         sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
 305         
 306         Desktop desktop = (context == null) 
 307                 ? Desktop.desktop
 308                 : (Desktop)context.get(Desktop.class);
 309 
 310         if (desktop == null) {
 311             desktop = new Desktop();
 312             if (context != null) {
 313                 context.put(Desktop.class, desktop);
 314             }
 315         }
 316 
 317         return desktop;
 318     }
 319 
 320     /**
 321      * Tests whether this class is supported on the current platform.
 322      * If it's supported, use {@link #getDesktop()} to retrieve an
 323      * instance.
 324      *
 325      * @return {@code true} if this class is supported on the
 326      *         current platform; {@code false} otherwise
 327      * @see #getDesktop()
 328      */
 329     public static boolean isDesktopSupported(){
 330         Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
 331         if (defaultToolkit instanceof SunToolkit) {
 332             return ((SunToolkit)defaultToolkit).isDesktopSupported();
 333         }
 334         return false;
 335     }
 336 
 337     /**
 338      * Tests whether an action is supported on the current platform.
 339      *
 340      * <p>Even when the platform supports an action, a file or URI may
 341      * not have a registered application for the action.  For example,
 342      * most of the platforms support the {@link Desktop.Action#OPEN}
 343      * action.  But for a specific file, there may not be an
 344      * application registered to open it.  In this case, {@link
 345      * #isSupported} may return {@code true}, but the corresponding
 346      * action method will throw an {@link IOException}.
 347      *
 348      * @param action the specified {@link Action}
 349      * @return {@code true} if the specified action is supported on
 350      *         the current platform; {@code false} otherwise
 351      * @see Desktop.Action
 352      */
 353     public boolean isSupported(Action action) {
 354         return peer.isSupported(action);
 355     }
 356 
 357     /**
 358      * Checks if the file is a valid file and readable.
 359      *
 360      * @throws SecurityException If a security manager exists and its
 361      *         {@link SecurityManager#checkRead(java.lang.String)} method
 362      *         denies read access to the file
 363      * @throws NullPointerException if file is null
 364      * @throws IllegalArgumentException if file doesn't exist
 365      */
 366     private static void checkFileValidation(File file){
 367         if (file == null) throw new NullPointerException("File must not be null");
 368 
 369         if (!file.exists()) {
 370             throw new IllegalArgumentException("The file: "
 371                     + file.getPath() + " doesn't exist.");
 372         }
 373 
 374         file.canRead();
 375     }
 376 
 377     /**
 378      * Checks if the action type is supported.
 379      *
 380      * @param actionType the action type in question
 381      * @throws UnsupportedOperationException if the specified action type is not
 382      *         supported on the current platform
 383      */
 384     private void checkActionSupport(Action actionType){
 385         if (!isSupported(actionType)) {
 386             throw new UnsupportedOperationException("The " + actionType.name()
 387                     + " action is not supported on the current platform!");
 388         }
 389     }
 390 
 391 
 392     /**
 393      *  Calls to the security manager's {@code checkPermission} method with
 394      *  an {@code AWTPermission("showWindowWithoutWarningBanner")}
 395      *  permission.
 396      */
 397     private void checkAWTPermission(){
 398         SecurityManager sm = System.getSecurityManager();
 399         if (sm != null) {
 400             sm.checkPermission(new AWTPermission(
 401                     "showWindowWithoutWarningBanner"));
 402         }
 403     }
 404 
 405     /**
 406      * Launches the associated application to open the file.
 407      *
 408      * <p> If the specified file is a directory, the file manager of
 409      * the current platform is launched to open it.
 410      *
 411      * @param file the file to be opened with the associated application
 412      * @throws NullPointerException if {@code file} is {@code null}
 413      * @throws IllegalArgumentException if the specified file doesn't
 414      * exist
 415      * @throws UnsupportedOperationException if the current platform
 416      * does not support the {@link Desktop.Action#OPEN} action
 417      * @throws IOException if the specified file has no associated
 418      * application or the associated application fails to be launched
 419      * @throws SecurityException if a security manager exists and its
 420      * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
 421      * method denies read access to the file, or it denies the
 422      * {@code AWTPermission("showWindowWithoutWarningBanner")}
 423      * permission, or the calling thread is not allowed to create a
 424      * subprocess
 425      * @see java.awt.AWTPermission
 426      */
 427     public void open(File file) throws IOException {
 428         checkAWTPermission();
 429         checkExec();
 430         checkActionSupport(Action.OPEN);
 431         checkFileValidation(file);
 432 
 433         peer.open(file);
 434     }
 435 
 436     /**
 437      * Launches the associated editor application and opens a file for
 438      * editing.
 439      *
 440      * @param file the file to be opened for editing
 441      * @throws NullPointerException if the specified file is {@code null}
 442      * @throws IllegalArgumentException if the specified file doesn't
 443      * exist
 444      * @throws UnsupportedOperationException if the current platform
 445      * does not support the {@link Desktop.Action#EDIT} action
 446      * @throws IOException if the specified file has no associated
 447      * editor, or the associated application fails to be launched
 448      * @throws SecurityException if a security manager exists and its
 449      * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
 450      * method denies read access to the file, or {@link
 451      * java.lang.SecurityManager#checkWrite(java.lang.String)} method
 452      * denies write access to the file, or it denies the
 453      * {@code AWTPermission("showWindowWithoutWarningBanner")}
 454      * permission, or the calling thread is not allowed to create a
 455      * subprocess
 456      * @see java.awt.AWTPermission
 457      */
 458     public void edit(File file) throws IOException {
 459         checkAWTPermission();
 460         checkExec();
 461         checkActionSupport(Action.EDIT);
 462         file.canWrite();
 463         checkFileValidation(file);
 464 
 465         peer.edit(file);
 466     }
 467 
 468     /**
 469      * Prints a file with the native desktop printing facility, using
 470      * the associated application's print command.
 471      *
 472      * @param file the file to be printed
 473      * @throws NullPointerException if the specified file is {@code
 474      * null}
 475      * @throws IllegalArgumentException if the specified file doesn't
 476      * exist
 477      * @throws UnsupportedOperationException if the current platform
 478      *         does not support the {@link Desktop.Action#PRINT} action
 479      * @throws IOException if the specified file has no associated
 480      * application that can be used to print it
 481      * @throws SecurityException if a security manager exists and its
 482      * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
 483      * method denies read access to the file, or its {@link
 484      * java.lang.SecurityManager#checkPrintJobAccess()} method denies
 485      * the permission to print the file, or the calling thread is not
 486      * allowed to create a subprocess
 487      */
 488     public void print(File file) throws IOException {
 489         checkExec();
 490         SecurityManager sm = System.getSecurityManager();
 491         if (sm != null) {
 492             sm.checkPrintJobAccess();
 493         }
 494         checkActionSupport(Action.PRINT);
 495         checkFileValidation(file);
 496 
 497         peer.print(file);
 498     }
 499 
 500     /**
 501      * Launches the default browser to display a {@code URI}.
 502      * If the default browser is not able to handle the specified
 503      * {@code URI}, the application registered for handling
 504      * {@code URIs} of the specified type is invoked. The application
 505      * is determined from the protocol and path of the {@code URI}, as
 506      * defined by the {@code URI} class.
 507      * <p>
 508      * If the calling thread does not have the necessary permissions,
 509      * and this is invoked from within an applet,
 510      * {@code AppletContext.showDocument()} is used. Similarly, if the calling
 511      * does not have the necessary permissions, and this is invoked from within
 512      * a Java Web Started application, {@code BasicService.showDocument()}
 513      * is used.
 514      *
 515      * @param uri the URI to be displayed in the user default browser
 516      * @throws NullPointerException if {@code uri} is {@code null}
 517      * @throws UnsupportedOperationException if the current platform
 518      * does not support the {@link Desktop.Action#BROWSE} action
 519      * @throws IOException if the user default browser is not found,
 520      * or it fails to be launched, or the default handler application
 521      * failed to be launched
 522      * @throws SecurityException if a security manager exists and it
 523      * denies the
 524      * {@code AWTPermission("showWindowWithoutWarningBanner")}
 525      * permission, or the calling thread is not allowed to create a
 526      * subprocess; and not invoked from within an applet or Java Web Started
 527      * application
 528      * @throws IllegalArgumentException if the necessary permissions
 529      * are not available and the URI can not be converted to a {@code URL}
 530      * @see java.net.URI
 531      * @see java.awt.AWTPermission
 532      * @see java.applet.AppletContext
 533      */
 534     public void browse(URI uri) throws IOException {
 535         SecurityException securityException = null;
 536         try {
 537             checkAWTPermission();
 538             checkExec();
 539         } catch (SecurityException e) {
 540             securityException = e;
 541         }
 542         checkActionSupport(Action.BROWSE);
 543         if (uri == null) {
 544             throw new NullPointerException();
 545         }
 546         if (securityException == null) {
 547             peer.browse(uri);
 548             return;
 549         }
 550 
 551         // Calling thread doesn't have necessary privileges.
 552         // Delegate to DesktopBrowse so that it can work in
 553         // applet/webstart.
 554         URL url = null;
 555         try {
 556             url = uri.toURL();
 557         } catch (MalformedURLException e) {
 558             throw new IllegalArgumentException("Unable to convert URI to URL", e);
 559         }
 560         sun.awt.DesktopBrowse db = sun.awt.DesktopBrowse.getInstance();
 561         if (db == null) {
 562             // Not in webstart/applet, throw the exception.
 563             throw securityException;
 564         }
 565         db.browse(url);
 566     }
 567 
 568     /**
 569      * Launches the mail composing window of the user default mail
 570      * client.
 571      *
 572      * @throws UnsupportedOperationException if the current platform
 573      * does not support the {@link Desktop.Action#MAIL} action
 574      * @throws IOException if the user default mail client is not
 575      * found, or it fails to be launched
 576      * @throws SecurityException if a security manager exists and it
 577      * denies the
 578      * {@code AWTPermission("showWindowWithoutWarningBanner")}
 579      * permission, or the calling thread is not allowed to create a
 580      * subprocess
 581      * @see java.awt.AWTPermission
 582      */
 583     public void mail() throws IOException {
 584         checkAWTPermission();
 585         checkExec();
 586         checkActionSupport(Action.MAIL);
 587         URI mailtoURI = null;
 588         try{
 589             mailtoURI = new URI("mailto:?");
 590             peer.mail(mailtoURI);
 591         } catch (URISyntaxException e){
 592             // won't reach here.
 593         }
 594     }
 595 
 596     /**
 597      * Launches the mail composing window of the user default mail
 598      * client, filling the message fields specified by a {@code
 599      * mailto:} URI.
 600      *
 601      * <p> A {@code mailto:} URI can specify message fields
 602      * including <i>"to"</i>, <i>"cc"</i>, <i>"subject"</i>,
 603      * <i>"body"</i>, etc.  See <a
 604      * href="http://www.ietf.org/rfc/rfc2368.txt">The mailto URL
 605      * scheme (RFC 2368)</a> for the {@code mailto:} URI specification
 606      * details.
 607      *
 608      * @param mailtoURI the specified {@code mailto:} URI
 609      * @throws NullPointerException if the specified URI is {@code
 610      * null}
 611      * @throws IllegalArgumentException if the URI scheme is not
 612      *         {@code "mailto"}
 613      * @throws UnsupportedOperationException if the current platform
 614      * does not support the {@link Desktop.Action#MAIL} action
 615      * @throws IOException if the user default mail client is not
 616      * found or fails to be launched
 617      * @throws SecurityException if a security manager exists and it
 618      * denies the
 619      * {@code AWTPermission("showWindowWithoutWarningBanner")}
 620      * permission, or the calling thread is not allowed to create a
 621      * subprocess
 622      * @see java.net.URI
 623      * @see java.awt.AWTPermission
 624      */
 625     public  void mail(URI mailtoURI) throws IOException {
 626         checkAWTPermission();
 627         checkExec();
 628         checkActionSupport(Action.MAIL);
 629         if (mailtoURI == null) throw new NullPointerException();
 630 
 631         if (!"mailto".equalsIgnoreCase(mailtoURI.getScheme())) {
 632             throw new IllegalArgumentException("URI scheme is not \"mailto\"");
 633         }
 634 
 635         peer.mail(mailtoURI);
 636     }
 637 
 638     private void checkExec() throws SecurityException {
 639         SecurityManager sm = System.getSecurityManager();
 640         if (sm != null) {
 641             sm.checkPermission(new FilePermission("<<ALL FILES>>",
 642                     SecurityConstants.FILE_EXECUTE_ACTION));
 643         }
 644     }
 645 
 646     private void checkRead() throws SecurityException {
 647         SecurityManager sm = System.getSecurityManager();
 648         if (sm != null) {
 649             sm.checkPermission(new FilePermission("<<ALL FILES>>",
 650                     SecurityConstants.FILE_READ_ACTION));
 651         }
 652     }
 653 
 654     private void checkDelete() throws SecurityException {
 655         SecurityManager sm = System.getSecurityManager();
 656         if (sm != null) {
 657             sm.checkPermission(new FilePermission("<<ALL FILES>>",
 658                     SecurityConstants.FILE_DELETE_ACTION));
 659         }
 660     }
 661 
 662     private void checkQuitPermission() {
 663         SecurityManager sm = System.getSecurityManager();
 664         if (sm != null) {
 665             sm.checkExit(0);
 666         }
 667     }
 668 
 669     /**
 670      * Adds sub-types of {@link SystemEventListener} to listen for notifications
 671      * from the native system.
 672      *
 673      * Has no effect if SystemEventListener's sub-type is unsupported on the current
 674      * platform.
 675      *
 676      * @param listener listener
 677      *
 678      * @throws SecurityException if a security manager exists and it
 679      * denies the
 680      * {@code RuntimePermission("canProcessApplicationEvents")}
 681      * permission
 682      *
 683      * @see java.awt.desktop.AppForegroundListener
 684      * @see java.awt.desktop.AppHiddenListener
 685      * @see java.awt.desktop.AppReopenedListener
 686      * @see java.awt.desktop.ScreenSleepListener
 687      * @see java.awt.desktop.SystemSleepListener
 688      * @see java.awt.desktop.UserSessionListener
 689      * @since 9
 690      */
 691     public void addAppEventListener(final SystemEventListener listener) {
 692         checkEventsProcessingPermission();
 693         peer.addAppEventListener(listener);
 694     }
 695 
 696     /**
 697      * Removes sub-types of {@link SystemEventListener} to listen for notifications
 698      * from the native system.
 699      *
 700      * Has no effect if SystemEventListener's sub-type is unsupported on  the current
 701      * platform.
 702      *
 703      * @param listener listener
 704      *
 705      * @throws SecurityException if a security manager exists and it
 706      * denies the
 707      * {@code RuntimePermission("canProcessApplicationEvents")}
 708      * permission
 709      *
 710      * @see java.awt.desktop.AppForegroundListener
 711      * @see java.awt.desktop.AppHiddenListener
 712      * @see java.awt.desktop.AppReopenedListener
 713      * @see java.awt.desktop.ScreenSleepListener
 714      * @see java.awt.desktop.SystemSleepListener
 715      * @see java.awt.desktop.UserSessionListener
 716      * @since 9
 717      */
 718     public void removeAppEventListener(final SystemEventListener listener) {
 719         checkEventsProcessingPermission();
 720         peer.removeAppEventListener(listener);
 721     }
 722 
 723     /**
 724      * Installs a handler to show a custom About window for your application.
 725      * <p>
 726      * Setting the {@link java.awt.desktop.AboutHandler} to {@code null} reverts it to the
 727      * default behavior.
 728      *
 729      * @param aboutHandler the handler to respond to the
 730      * {@link java.awt.desktop.AboutHandler#handleAbout} )} message
 731      *
 732      * @throws SecurityException if a security manager exists and it
 733      * denies the
 734      * {@code RuntimePermission("canProcessApplicationEvents")}
 735      * permission
 736      * @throws UnsupportedOperationException if the current platform
 737      * does not support the {@link Desktop.Action#APP_ABOUT} action
 738      *
 739      * @since 9
 740      */
 741     public void setAboutHandler(final AboutHandler aboutHandler) {
 742         checkEventsProcessingPermission();
 743         checkActionSupport(Action.APP_ABOUT);
 744         peer.setAboutHandler(aboutHandler);
 745     }
 746 
 747     /**
 748      * Installs a handler to show a custom Preferences window for your
 749      * application.
 750      * <p>
 751      * Setting the {@link PreferencesHandler} to {@code null} reverts it to
 752      * the default behavior
 753      *
 754      * @param preferencesHandler the handler to respond to the
 755      * {@link PreferencesHandler#handlePreferences(PreferencesEvent)}
 756      *
 757      * @throws SecurityException if a security manager exists and it
 758      * denies the
 759      * {@code RuntimePermission("canProcessApplicationEvents")} permission
 760      * @throws UnsupportedOperationException if the current platform
 761      * does not support the {@link Desktop.Action#APP_PREFERENCES} action
 762      * @since 9
 763      */
 764     public void setPreferencesHandler(final PreferencesHandler preferencesHandler) {
 765         checkEventsProcessingPermission();
 766         checkActionSupport(Action.APP_PREFERENCES);
 767         peer.setPreferencesHandler(preferencesHandler);
 768     }
 769 
 770     /**
 771      * Installs the handler which is notified when the application is asked to
 772      * open a list of files.
 773      *
 774      * @implNote Please note that for Mac OS, notifications
 775      * are only sent if the Java app is a bundled application,
 776      * with a {@code CFBundleDocumentTypes} array present in its
 777      * Info.plist. See the
 778      * <a href="http://developer.apple.com/mac/library/documentation/General/Reference/InfoPlistKeyReference">
 779      * Info.plist Key Reference</a> for more information about adding a
 780      * {@code CFBundleDocumentTypes} key to your app's Info.plist.
 781      *
 782      * @param openFileHandler handler
 783      *
 784      * @throws SecurityException if a security manager exists and its
 785      * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
 786      * method denies read access to the files, or it denies the
 787      * {@code RuntimePermission("canProcessApplicationEvents")}
 788      * permission, or the calling thread is not allowed to create a
 789      * subprocess
 790      * @throws UnsupportedOperationException if the current platform
 791      * does not support the {@link Desktop.Action#APP_OPEN_FILE} action
 792      * @since 9
 793      */
 794     public void setOpenFileHandler(final OpenFilesHandler openFileHandler) {
 795         checkEventsProcessingPermission();
 796         checkExec();
 797         checkRead();
 798         checkActionSupport(Action.APP_OPEN_FILE);
 799         peer.setOpenFileHandler(openFileHandler);
 800     }
 801 
 802     /**
 803      * Installs the handler which is notified when the application is asked to
 804      * print a list of files.
 805      *
 806      * @implNote Please note that for Mac OS, notifications
 807      * are only sent if the Java app is a bundled application,
 808      * with a {@code CFBundleDocumentTypes} array present in its
 809      * Info.plist. See the
 810      * <a href="http://developer.apple.com/mac/library/documentation/General/Reference/InfoPlistKeyReference">
 811      * Info.plist Key Reference</a> for more information about adding a
 812      * {@code CFBundleDocumentTypes} key to your app's Info.plist.
 813      *
 814      * @param printFileHandler handler
 815      * @throws SecurityException if a security manager exists and its
 816      * {@link java.lang.SecurityManager#checkPrintJobAccess()} method denies
 817      * the permission to print or it denies the
 818      * {@code RuntimePermission("canProcessApplicationEvents")} permission
 819      * @throws UnsupportedOperationException if the current platform
 820      * does not support the {@link Desktop.Action#APP_PRINT_FILE} action
 821      * @since 9
 822      */
 823     public void setPrintFileHandler(final PrintFilesHandler printFileHandler) {
 824         checkEventsProcessingPermission();
 825         SecurityManager sm = System.getSecurityManager();
 826         if (sm != null) {
 827             sm.checkPrintJobAccess();
 828         }
 829         checkActionSupport(Action.APP_PRINT_FILE);
 830         peer.setPrintFileHandler(printFileHandler);
 831     }
 832 
 833     /**
 834      * Installs the handler which is notified when the application is asked to
 835      * open a URL.
 836      *
 837      * Setting the handler to {@code null} causes all
 838      * {@link OpenURIHandler#openURI(AppEvent.OpenURIEvent)} requests to be
 839      * enqueued until another handler is set.
 840      *
 841      * @implNote Please note that for Mac OS, notifications
 842      * are only sent if the Java app is a bundled application,
 843      * with a {@code CFBundleDocumentTypes} array present in its
 844      * Info.plist. See the
 845      * <a href="http://developer.apple.com/mac/library/documentation/General/Reference/InfoPlistKeyReference">
 846      * Info.plist Key Reference</a> for more information about adding a
 847      * {@code CFBundleDocumentTypes} key to your app's Info.plist.
 848      *
 849      * @param openURIHandler handler
 850      *
 851      * {@code RuntimePermission("canProcessApplicationEvents")}
 852      * permission, or the calling thread is not allowed to create a
 853      * subprocess
 854      * @throws UnsupportedOperationException if the current platform
 855      * does not support the {@link Desktop.Action#APP_OPEN_URI} action
 856      * @since 9
 857      */
 858     public void setOpenURIHandler(final OpenURIHandler openURIHandler) {
 859         checkEventsProcessingPermission();
 860         checkExec();
 861         checkActionSupport(Action.APP_OPEN_URI);
 862         peer.setOpenURIHandler(openURIHandler);
 863     }
 864 
 865     /**
 866      * Installs the handler which determines if the application should quit. The
 867      * handler is passed a one-shot {@link java.awt.desktop.QuitResponse} which can cancel or
 868      * proceed with the quit. Setting the handler to {@code null} causes
 869      * all quit requests to directly perform the default {@link QuitStrategy}.
 870      *
 871      * @param quitHandler the handler that is called when the application is
 872      * asked to quit
 873      *
 874      * @throws SecurityException if a security manager exists and it
 875      * will not allow the caller to invoke {@code System.exit} or it denies the
 876      * {@code RuntimePermission("canProcessApplicationEvents")} permission
 877      * @throws UnsupportedOperationException if the current platform
 878      * does not support the {@link Desktop.Action#APP_QUIT_HANDLER} action
 879      * @since 9
 880      */
 881     public void setQuitHandler(final QuitHandler quitHandler) {
 882         checkEventsProcessingPermission();
 883         checkQuitPermission();
 884         checkActionSupport(Action.APP_QUIT_HANDLER);
 885         peer.setQuitHandler(quitHandler);
 886     }
 887 
 888     /**
 889      * Sets the default strategy used to quit this application. The default is
 890      * calling SYSTEM_EXIT_0.
 891      *
 892      * @param strategy the way this application should be shutdown
 893      *
 894      * @throws SecurityException if a security manager exists and it
 895      * will not allow the caller to invoke {@code System.exit} or it denies the
 896      * {@code RuntimePermission("canProcessApplicationEvents")} permission
 897      * @throws UnsupportedOperationException if the current platform
 898      * does not support the {@link Desktop.Action#APP_QUIT_STRATEGY} action
 899      * @see QuitStrategy
 900      * @since 9
 901      */
 902     public void setQuitStrategy(final QuitStrategy strategy) {
 903         checkEventsProcessingPermission();
 904         checkQuitPermission();
 905         checkActionSupport(Action.APP_QUIT_STRATEGY);
 906         peer.setQuitStrategy(strategy);
 907     }
 908 
 909     /**
 910      * Enables this application to be suddenly terminated.
 911      *
 912      * Call this method to indicate your application's state is saved, and
 913      * requires no notification to be terminated. Letting your application
 914      * remain terminatable improves the user experience by avoiding re-paging in
 915      * your application when it's asked to quit.
 916      *
 917      * <b>Note: enabling sudden termination will allow your application to be
 918      * quit without notifying your QuitHandler, or running any shutdown
 919      * hooks.</b>
 920      * E.g. user-initiated Cmd-Q, logout, restart, or shutdown requests will
 921      * effectively "kill -KILL" your application.
 922      *
 923      * @throws SecurityException if a security manager exists and it
 924      * will not allow the caller to invoke {@code System.exit} or it denies the
 925      * {@code RuntimePermission("canProcessApplicationEvents")} permission
 926      * @throws UnsupportedOperationException if the current platform
 927      * does not support the {@link Desktop.Action#APP_SUDDEN_TERMINATION} action
 928      * @see #disableSuddenTermination()
 929      * @since 9
 930      */
 931     public void enableSuddenTermination() {
 932         checkEventsProcessingPermission();
 933         checkQuitPermission();
 934         checkActionSupport(Action.APP_SUDDEN_TERMINATION);
 935         peer.enableSuddenTermination();
 936     }
 937 
 938     /**
 939      * Prevents this application from being suddenly terminated.
 940      *
 941      * Call this method to indicate that your application has unsaved state, and
 942      * may not be terminated without notification.
 943      *
 944      * @throws SecurityException if a security manager exists and it
 945      * will not allow the caller to invoke {@code System.exit} or it denies the
 946      * {@code RuntimePermission("canProcessApplicationEvents")} permission
 947      * @throws UnsupportedOperationException if the current platform
 948      * does not support the {@link Desktop.Action#APP_SUDDEN_TERMINATION} action
 949      * @see #enableSuddenTermination()
 950      * @since 9
 951      */
 952     public void disableSuddenTermination() {
 953         checkEventsProcessingPermission();
 954         checkQuitPermission();
 955         checkActionSupport(Action.APP_SUDDEN_TERMINATION);
 956         peer.disableSuddenTermination();
 957     }
 958 
 959     /**
 960      * Requests this application to move to the foreground.
 961      *
 962      * @param allWindows if all windows of this application should be moved to
 963      * the foreground, or only the foremost one
 964      * @throws SecurityException if a security manager exists and it denies the
 965      * {@code RuntimePermission("canProcessApplicationEvents")} permission.
 966      * @throws UnsupportedOperationException if the current platform
 967      * does not support the {@link Desktop.Action#APP_REQUEST_FOREGROUND} action
 968      * @since 9
 969      */
 970     public void requestForeground(final boolean allWindows) {
 971         checkEventsProcessingPermission();
 972         checkActionSupport(Action.APP_REQUEST_FOREGROUND);
 973         peer.requestForeground(allWindows);
 974     }
 975 
 976     /**
 977      * Opens the native help viewer application.
 978      *
 979      * @implNote Please note that for Mac OS, it opens the native help viewer
 980      * application if a Help Book has been added to the application bundler
 981      * and registered in the Info.plist with CFBundleHelpBookFolder
 982      *
 983      * @throws SecurityException if a security manager exists and it denies the
 984      * {@code RuntimePermission("canProcessApplicationEvents")} permission.
 985      * @throws UnsupportedOperationException if the current platform
 986      * does not support the {@link Desktop.Action#APP_HELP_VIEWER} action
 987      * @since 9
 988      */
 989     public void openHelpViewer() {
 990         checkEventsProcessingPermission();
 991         checkActionSupport(Action.APP_HELP_VIEWER);
 992         peer.openHelpViewer();
 993     }
 994 
 995     /**
 996      * Sets the default menu bar to use when there are no active frames.
 997      *
 998      * @implNote Aqua Look and Feel should be active to support this on Mac OS.
 999      *
1000      * @param menuBar to use when no other frames are active
1001      * @throws SecurityException if a security manager exists and it denies the
1002      * {@code RuntimePermission("canProcessApplicationEvents")} permission.
1003      * @throws UnsupportedOperationException if the current platform
1004      * does not support the {@link Desktop.Action#APP_MENU_BAR} action
1005      * @since 9
1006      */
1007     public void setDefaultMenuBar(final JMenuBar menuBar) {
1008         checkEventsProcessingPermission();
1009         checkActionSupport(Action.APP_MENU_BAR);
1010         peer.setDefaultMenuBar(menuBar);
1011     }
1012 
1013     /**
1014      * Opens a folder containing the {@code file} and selects it
1015      * in a default system file manager.
1016      * @param file the file
1017      * @throws SecurityException If a security manager exists and its
1018      *         {@link SecurityManager#checkRead(java.lang.String)} method
1019      *         denies read access to the file
1020      * @throws UnsupportedOperationException if the current platform
1021      *         does not support the {@link Desktop.Action#BROWSE_FILE_DIR} action
1022      * @throws NullPointerException if {@code file} is {@code null}
1023      * @throws IllegalArgumentException if the specified file doesn't
1024      * exist
1025      * @since 9
1026      */
1027     public void browseFileDirectory(File file) {
1028         checkRead();
1029         checkActionSupport(Action.BROWSE_FILE_DIR);
1030         checkFileValidation(file);
1031         peer.browseFileDirectory(file);
1032     }
1033 
1034     /**
1035      * Moves the specified file to the trash.
1036      *
1037      * @param file the file
1038      * @return returns true if successfully moved the file to the trash.
1039      * @throws SecurityException If a security manager exists and its
1040      *         {@link SecurityManager#checkDelete(java.lang.String)} method
1041      *         denies deletion of the file
1042      * @throws UnsupportedOperationException if the current platform
1043      *         does not support the {@link Desktop.Action#MOVE_TO_TRASH} action
1044      * @throws NullPointerException if {@code file} is {@code null}
1045      * @throws IllegalArgumentException if the specified file doesn't
1046      * exist
1047      *
1048      * @since 9
1049      */
1050     public boolean moveToTrash(final File file) {
1051         checkDelete();
1052         checkActionSupport(Action.MOVE_TO_TRASH);
1053         checkFileValidation(file);
1054         return peer.moveToTrash(file);
1055     }
1056 }