source: josm/trunk/src/org/openstreetmap/josm/tools/PlatformHook.java @ 12846

Last change on this file since 12846 was 12846, checked in by bastiK, 3 months ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.GraphicsEnvironment;
5import java.awt.Toolkit;
6import java.awt.event.KeyEvent;
7import java.io.BufferedReader;
8import java.io.File;
9import java.io.IOException;
10import java.io.InputStreamReader;
11import java.nio.charset.StandardCharsets;
12import java.security.KeyStore;
13import java.security.KeyStoreException;
14import java.security.NoSuchAlgorithmException;
15import java.security.cert.CertificateException;
16import java.security.cert.X509Certificate;
17import java.text.DateFormat;
18import java.util.Date;
19import java.util.List;
20
21import org.openstreetmap.josm.data.projection.datum.NTV2Proj4DirGridShiftFileSource;
22import org.openstreetmap.josm.io.CertificateAmendment.CertAmend;
23import org.openstreetmap.josm.spi.preferences.Config;
24import org.openstreetmap.josm.tools.date.DateUtils;
25
26/**
27 * This interface allows platform (operating system) dependent code
28 * to be bundled into self-contained classes.
29 * @since 1023
30 */
31public interface PlatformHook {
32
33    /**
34     * Visitor to construct a PlatformHook from a given {@link Platform} object.
35     */
36    PlatformVisitor<PlatformHook> CONSTRUCT_FROM_PLATFORM = new PlatformVisitor<PlatformHook>() {
37        @Override
38        public PlatformHook visitUnixoid() {
39            return new PlatformHookUnixoid();
40        }
41
42        @Override
43        public PlatformHook visitWindows() {
44            return new PlatformHookWindows();
45        }
46
47        @Override
48        public PlatformHook visitOsx() {
49            return new PlatformHookUnixoid();
50        }
51    };
52
53    /**
54     * Get the platform corresponding to this platform hook.
55     * @return the platform corresponding to this platform hook
56     */
57    Platform getPlatform();
58
59    /**
60      * The preStartupHook will be called extremly early. It is
61      * guaranteed to be called before the GUI setup has started.
62      *
63      * Reason: On OSX we need to inform the Swing libraries
64      * that we want to be integrated with the OS before we setup our GUI.
65      */
66    default void preStartupHook() {
67        // Do nothing
68    }
69
70    /**
71      * The afterPrefStartupHook will be called early, but after
72      * the preferences have been loaded and basic processing of
73      * command line arguments is finished.
74      * It is guaranteed to be called before the GUI setup has started.
75      */
76    default void afterPrefStartupHook() {
77        // Do nothing
78    }
79
80    /**
81      * The startupHook will be called early, but after the GUI
82      * setup has started.
83      *
84      * Reason: On OSX we need to register some callbacks with the
85      * OS, so we'll receive events from the system menu.
86     * @param callback Java expiration callback, providing GUI feedback
87     * @since 12270 (signature)
88      */
89    default void startupHook(JavaExpirationCallback callback) {
90        // Do nothing
91    }
92
93    /**
94      * The openURL hook will be used to open an URL in the
95      * default web browser.
96     * @param url The URL to open
97     * @throws IOException if any I/O error occurs
98      */
99    void openUrl(String url) throws IOException;
100
101    /**
102      * The initSystemShortcuts hook will be called by the
103      * Shortcut class after the modifier groups have been read
104      * from the config, but before any shortcuts are read from
105      * it or registered from within the application.
106      *
107      * Please note that you are not allowed to register any
108      * shortuts from this hook, but only "systemCuts"!
109      *
110      * BTW: SystemCuts should be named "system:&lt;whatever&gt;",
111      * and it'd be best if sou'd recycle the names already used
112      * by the Windows and OSX hooks. Especially the later has
113      * really many of them.
114      *
115      * You should also register any and all shortcuts that the
116      * operation system handles itself to block JOSM from trying
117      * to use them---as that would just not work. Call setAutomatic
118      * on them to prevent the keyboard preferences from allowing the
119      * user to change them.
120      */
121    void initSystemShortcuts();
122
123    /**
124      * The makeTooltip hook will be called whenever a tooltip for
125      * a menu or button is created.
126      *
127      * Tooltips are usually not system dependent, unless the
128      * JVM is too dumb to provide correct names for all the keys.
129      *
130      * Some LAFs don't understand HTML, such as the OSX LAFs.
131      *
132     * @param name Tooltip text to display
133     * @param sc Shortcut associated (to display accelerator between parenthesis)
134     * @return Full tooltip text (name + accelerator)
135      */
136    default String makeTooltip(String name, Shortcut sc) {
137        StringBuilder result = new StringBuilder();
138        result.append("<html>").append(name);
139        if (sc != null && !sc.getKeyText().isEmpty()) {
140            result.append(" <font size='-2'>(")
141                  .append(sc.getKeyText())
142                  .append(")</font>");
143        }
144        return result.append("&nbsp;</html>").toString();
145    }
146
147    /**
148     * Returns the default LAF to be used on this platform to look almost as a native application.
149     * @return The default native LAF for this platform
150     */
151    String getDefaultStyle();
152
153    /**
154     * Determines if the platform allows full-screen.
155     * @return {@code true} if full screen is allowed, {@code false} otherwise
156     */
157    default boolean canFullscreen() {
158        return !GraphicsEnvironment.isHeadless() &&
159                GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().isFullScreenSupported();
160    }
161
162    /**
163     * Renames a file.
164     * @param from Source file
165     * @param to Target file
166     * @return {@code true} if the file has been renamed, {@code false} otherwise
167     */
168    default boolean rename(File from, File to) {
169        return from.renameTo(to);
170    }
171
172    /**
173     * Returns a detailed OS description (at least family + version).
174     * @return A detailed OS description.
175     * @since 5850
176     */
177    String getOSDescription();
178
179    /**
180     * Returns OS build number.
181     * @return OS build number.
182     * @since 12217
183     */
184    default String getOSBuildNumber() {
185        return "";
186    }
187
188    /**
189     * Setup system keystore to add JOSM HTTPS certificate (for remote control).
190     * @param entryAlias The entry alias to use
191     * @param trustedCert the JOSM certificate for localhost
192     * @return {@code true} if something has changed as a result of the call (certificate installation, etc.)
193     * @throws KeyStoreException in case of error
194     * @throws IOException in case of error
195     * @throws CertificateException in case of error
196     * @throws NoSuchAlgorithmException in case of error
197     * @since 7343
198     */
199    default boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
200            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
201        // TODO setup HTTPS certificate on Unix and OS X systems
202        return false;
203    }
204
205    /**
206     * Returns the {@code X509Certificate} matching the given certificate amendment information.
207     * @param certAmend certificate amendment
208     * @return the {@code X509Certificate} matching the given certificate amendment information, or {@code null}
209     * @throws KeyStoreException in case of error
210     * @throws IOException in case of error
211     * @throws CertificateException in case of error
212     * @throws NoSuchAlgorithmException in case of error
213     * @since 11943
214     */
215    default X509Certificate getX509Certificate(CertAmend certAmend)
216            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
217        return null;
218    }
219
220    /**
221     * Executes a native command and returns the first line of standard output.
222     * @param command array containing the command to call and its arguments.
223     * @return first stripped line of standard output
224     * @throws IOException if an I/O error occurs
225     * @since 12217
226     */
227    default String exec(String... command) throws IOException {
228        Process p = Runtime.getRuntime().exec(command);
229        try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
230            return Utils.strip(input.readLine());
231        }
232    }
233
234    /**
235     * Returns the platform-dependent default cache directory.
236     * @return the platform-dependent default cache directory
237     * @since 7829
238     */
239    File getDefaultCacheDirectory();
240
241    /**
242     * Returns the platform-dependent default preferences directory.
243     * @return the platform-dependent default preferences directory
244     * @since 7831
245     */
246    File getDefaultPrefDirectory();
247
248    /**
249     * Returns the platform-dependent default user data directory.
250     * @return the platform-dependent default user data directory
251     * @since 7834
252     */
253    File getDefaultUserDataDirectory();
254
255    /**
256     * Returns the list of platform-dependent default datum shifting directories for the PROJ.4 library.
257     * @return the list of platform-dependent default datum shifting directories for the PROJ.4 library
258     * @since 11642
259     */
260    default List<File> getDefaultProj4NadshiftDirectories() {
261        return getPlatform().accept(NTV2Proj4DirGridShiftFileSource.getInstance());
262    }
263
264    /**
265     * Determines if the JVM is OpenJDK-based.
266     * @return {@code true} if {@code java.home} contains "openjdk", {@code false} otherwise
267     * @since 12219
268     */
269    default boolean isOpenJDK() {
270        String javaHome = System.getProperty("java.home");
271        return javaHome != null && javaHome.contains("openjdk");
272    }
273
274    /**
275     * Returns extended modifier key used as the appropriate accelerator key for menu shortcuts.
276     * It is advised everywhere to use {@link Toolkit#getMenuShortcutKeyMask()} to get the cross-platform modifier, but:
277     * <ul>
278     * <li>it returns KeyEvent.CTRL_MASK instead of KeyEvent.CTRL_DOWN_MASK. We used the extended
279     *    modifier for years, and Oracle recommends to use it instead, so it's best to keep it</li>
280     * <li>the method throws a HeadlessException ! So we would need to handle it for unit tests anyway</li>
281     * </ul>
282     * @return extended modifier key used as the appropriate accelerator key for menu shortcuts
283     * @since 12748 (as a replacement to {@code GuiHelper.getMenuShortcutKeyMaskEx()})
284     */
285    default int getMenuShortcutKeyMaskEx() {
286        return KeyEvent.CTRL_DOWN_MASK;
287    }
288
289    /**
290     * Called when an outdated version of Java is detected at startup.
291     * @since 12270
292     */
293    @FunctionalInterface
294    interface JavaExpirationCallback {
295        /**
296         * Asks user to update its version of Java.
297         * @param updVersion target update version
298         * @param url download URL
299         * @param major true for a migration towards a major version of Java (8:9), false otherwise
300         * @param eolDate the EOL/expiration date
301         */
302        void askUpdateJava(String updVersion, String url, String eolDate, boolean major);
303    }
304
305    /**
306     * Checks if the running version of Java has expired, proposes to user to update it if needed.
307     * @param callback Java expiration callback
308     * @since 12270 (signature)
309     * @since 12219
310     */
311    default void checkExpiredJava(JavaExpirationCallback callback) {
312        Date expiration = Utils.getJavaExpirationDate();
313        if (expiration != null && expiration.before(new Date())) {
314            String version = Utils.getJavaLatestVersion();
315            callback.askUpdateJava(version != null ? version : "latest",
316                    Config.getPref().get("java.update.url", "https://www.java.com/download"),
317                    DateUtils.getDateFormat(DateFormat.MEDIUM).format(expiration), false);
318        }
319    }
320
321    /**
322     * Called when interfacing with native OS functions. Currently only used with macOS.
323     * The callback must perform all GUI-related tasks associated to an OS request.
324     * The non-GUI, platform-specific tasks, are usually performed by the {@code PlatformHook}.
325     * @since 12695
326     */
327    interface NativeOsCallback {
328        /**
329         * macOS: Called when JOSM is asked to open a list of files.
330         * @param files list of files to open
331         */
332        void openFiles(List<File> files);
333
334        /**
335         * macOS: Invoked when JOSM is asked to quit.
336         * @return {@code true} if JOSM has been closed, {@code false} if the user has cancelled the operation.
337         */
338        boolean handleQuitRequest();
339
340        /**
341         * macOS: Called when JOSM is asked to show it's about dialog.
342         */
343        void handleAbout();
344
345        /**
346         * macOS: Called when JOSM is asked to show it's preferences UI.
347         */
348        void handlePreferences();
349    }
350
351    /**
352     * Registers the native OS callback. Currently only needed for macOS.
353     * @param callback the native OS callback
354     * @since 12695
355     */
356    default void setNativeOsCallback(NativeOsCallback callback) {
357        // To be implemented if needed
358    }
359}
Note: See TracBrowser for help on using the repository browser.