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