[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[1023] | 2 | package org.openstreetmap.josm.tools;
|
---|
| 3 |
|
---|
[4998] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
[6682] | 6 | import java.awt.Desktop;
|
---|
[6443] | 7 | import java.awt.Dimension;
|
---|
[4203] | 8 | import java.awt.GraphicsEnvironment;
|
---|
[1023] | 9 | import java.awt.event.KeyEvent;
|
---|
[5850] | 10 | import java.io.BufferedReader;
|
---|
[8015] | 11 | import java.io.BufferedWriter;
|
---|
[4153] | 12 | import java.io.File;
|
---|
[8015] | 13 | import java.io.FileInputStream;
|
---|
[1023] | 14 | import java.io.IOException;
|
---|
[5850] | 15 | import java.io.InputStreamReader;
|
---|
[8015] | 16 | import java.io.OutputStream;
|
---|
| 17 | import java.io.OutputStreamWriter;
|
---|
| 18 | import java.io.Writer;
|
---|
[6682] | 19 | import java.net.URI;
|
---|
| 20 | import java.net.URISyntaxException;
|
---|
[7082] | 21 | import java.nio.charset.StandardCharsets;
|
---|
[8015] | 22 | import java.nio.file.FileSystems;
|
---|
[7314] | 23 | import java.nio.file.Files;
|
---|
[7315] | 24 | import java.nio.file.Path;
|
---|
[7314] | 25 | import java.nio.file.Paths;
|
---|
[7206] | 26 | import java.security.KeyStore;
|
---|
| 27 | import java.security.KeyStoreException;
|
---|
| 28 | import java.security.NoSuchAlgorithmException;
|
---|
| 29 | import java.security.cert.CertificateException;
|
---|
[8015] | 30 | import java.util.ArrayList;
|
---|
[6103] | 31 | import java.util.Arrays;
|
---|
[8015] | 32 | import java.util.Collection;
|
---|
| 33 | import java.util.List;
|
---|
[8404] | 34 | import java.util.Locale;
|
---|
[8015] | 35 | import java.util.Properties;
|
---|
[1023] | 36 |
|
---|
[6443] | 37 | import javax.swing.JOptionPane;
|
---|
| 38 |
|
---|
[6310] | 39 | import org.openstreetmap.josm.Main;
|
---|
[8073] | 40 | import org.openstreetmap.josm.data.Preferences.pref;
|
---|
| 41 | import org.openstreetmap.josm.data.Preferences.writeExplicitly;
|
---|
[6443] | 42 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
---|
[6952] | 43 | import org.openstreetmap.josm.gui.util.GuiHelper;
|
---|
[6310] | 44 |
|
---|
[1023] | 45 | /**
|
---|
[7206] | 46 | * {@code PlatformHook} base implementation.
|
---|
[2376] | 47 | *
|
---|
[4849] | 48 | * Don't write (Main.platform instanceof PlatformHookUnixoid) because other platform
|
---|
| 49 | * hooks are subclasses of this class.
|
---|
[2376] | 50 | */
|
---|
[1023] | 51 | public class PlatformHookUnixoid implements PlatformHook {
|
---|
[6070] | 52 |
|
---|
[8015] | 53 | /**
|
---|
| 54 | * Simple data class to hold information about a font.
|
---|
| 55 | *
|
---|
| 56 | * Used for fontconfig.properties files.
|
---|
| 57 | */
|
---|
| 58 | public static class FontEntry {
|
---|
| 59 | /**
|
---|
[8073] | 60 | * The character subset. Basically a free identifier, but should be unique.
|
---|
[8015] | 61 | */
|
---|
| 62 | @pref
|
---|
| 63 | public String charset;
|
---|
[8073] | 64 |
|
---|
[8015] | 65 | /**
|
---|
| 66 | * Platform font name.
|
---|
| 67 | */
|
---|
| 68 | @pref @writeExplicitly
|
---|
| 69 | public String name = "";
|
---|
[8073] | 70 |
|
---|
[8015] | 71 | /**
|
---|
| 72 | * File name.
|
---|
| 73 | */
|
---|
| 74 | @pref @writeExplicitly
|
---|
| 75 | public String file = "";
|
---|
| 76 |
|
---|
[8073] | 77 | /**
|
---|
| 78 | * Constructs a new {@code FontEntry}.
|
---|
| 79 | */
|
---|
[8015] | 80 | public FontEntry() {
|
---|
| 81 | }
|
---|
| 82 |
|
---|
[8073] | 83 | /**
|
---|
| 84 | * Constructs a new {@code FontEntry}.
|
---|
| 85 | * @param charset The character subset. Basically a free identifier, but should be unique
|
---|
| 86 | * @param name Platform font name
|
---|
| 87 | * @param file File name
|
---|
| 88 | */
|
---|
[8015] | 89 | public FontEntry(String charset, String name, String file) {
|
---|
| 90 | this.charset = charset;
|
---|
| 91 | this.name = name;
|
---|
| 92 | this.file = file;
|
---|
| 93 | }
|
---|
| 94 | }
|
---|
| 95 |
|
---|
[5994] | 96 | private String osDescription;
|
---|
[6070] | 97 |
|
---|
[4897] | 98 | @Override
|
---|
[6443] | 99 | public void preStartupHook() {
|
---|
[1169] | 100 | }
|
---|
[4897] | 101 |
|
---|
| 102 | @Override
|
---|
[8015] | 103 | public void afterPrefStartupHook() {
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | @Override
|
---|
[1169] | 107 | public void startupHook() {
|
---|
| 108 | }
|
---|
[4897] | 109 |
|
---|
| 110 | @Override
|
---|
[1169] | 111 | public void openUrl(String url) throws IOException {
|
---|
[6682] | 112 | for (String program : Main.pref.getCollection("browser.unix",
|
---|
| 113 | Arrays.asList("xdg-open", "#DESKTOP#", "$BROWSER", "gnome-open", "kfmclient openURL", "firefox"))) {
|
---|
[1169] | 114 | try {
|
---|
[6682] | 115 | if ("#DESKTOP#".equals(program)) {
|
---|
| 116 | Desktop.getDesktop().browse(new URI(url));
|
---|
| 117 | } else if (program.startsWith("$")) {
|
---|
| 118 | program = System.getenv().get(program.substring(1));
|
---|
| 119 | Runtime.getRuntime().exec(new String[]{program, url});
|
---|
| 120 | } else {
|
---|
| 121 | Runtime.getRuntime().exec(new String[]{program, url});
|
---|
| 122 | }
|
---|
[1169] | 123 | return;
|
---|
[7004] | 124 | } catch (IOException | URISyntaxException e) {
|
---|
[6310] | 125 | Main.warn(e);
|
---|
[1169] | 126 | }
|
---|
| 127 | }
|
---|
| 128 | }
|
---|
[1023] | 129 |
|
---|
[4897] | 130 | @Override
|
---|
[1169] | 131 | public void initSystemShortcuts() {
|
---|
[8518] | 132 | // CHECKSTYLE.OFF: LineLength
|
---|
[4897] | 133 | // TODO: Insert system shortcuts here. See Windows and especially OSX to see how to.
|
---|
[8513] | 134 | for (int i = KeyEvent.VK_F1; i <= KeyEvent.VK_F12; ++i) {
|
---|
[8518] | 135 | Shortcut.registerSystemShortcut("screen:toogle"+i, tr("reserved"), i, KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)
|
---|
| 136 | .setAutomatic();
|
---|
[8513] | 137 | }
|
---|
[8518] | 138 | Shortcut.registerSystemShortcut("system:reset", tr("reserved"), KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)
|
---|
| 139 | .setAutomatic();
|
---|
| 140 | Shortcut.registerSystemShortcut("system:resetX", tr("reserved"), KeyEvent.VK_BACK_SPACE, KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)
|
---|
| 141 | .setAutomatic();
|
---|
| 142 | // CHECKSTYLE.ON: LineLength
|
---|
[1169] | 143 | }
|
---|
[6920] | 144 |
|
---|
[1169] | 145 | /**
|
---|
[2376] | 146 | * This should work for all platforms. Yeah, should.
|
---|
[8518] | 147 | * See PlatformHook.java for a list of reasons why this is implemented here...
|
---|
[2376] | 148 | */
|
---|
[4897] | 149 | @Override
|
---|
[1169] | 150 | public String makeTooltip(String name, Shortcut sc) {
|
---|
[8848] | 151 | StringBuilder result = new StringBuilder();
|
---|
| 152 | result.append("<html>").append(name);
|
---|
[8461] | 153 | if (sc != null && !sc.getKeyText().isEmpty()) {
|
---|
[8848] | 154 | result.append(' ')
|
---|
| 155 | .append("<font size='-2'>")
|
---|
| 156 | .append('(').append(sc.getKeyText()).append(')')
|
---|
| 157 | .append("</font>");
|
---|
[1169] | 158 | }
|
---|
[8848] | 159 | return result.append(" </html>").toString();
|
---|
[1169] | 160 | }
|
---|
[2371] | 161 |
|
---|
[4897] | 162 | @Override
|
---|
[2376] | 163 | public String getDefaultStyle() {
|
---|
[2371] | 164 | return "javax.swing.plaf.metal.MetalLookAndFeel";
|
---|
| 165 | }
|
---|
[4153] | 166 |
|
---|
| 167 | @Override
|
---|
[6443] | 168 | public boolean canFullscreen() {
|
---|
[7075] | 169 | return !GraphicsEnvironment.isHeadless() &&
|
---|
[8518] | 170 | GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().isFullScreenSupported();
|
---|
[4203] | 171 | }
|
---|
| 172 |
|
---|
| 173 | @Override
|
---|
[6443] | 174 | public boolean rename(File from, File to) {
|
---|
[4153] | 175 | return from.renameTo(to);
|
---|
| 176 | }
|
---|
[5850] | 177 |
|
---|
[6103] | 178 | /**
|
---|
[6951] | 179 | * Determines if the JVM is OpenJDK-based.
|
---|
| 180 | * @return {@code true} if {@code java.home} contains "openjdk", {@code false} otherwise
|
---|
| 181 | * @since 6951
|
---|
| 182 | */
|
---|
| 183 | public static boolean isOpenJDK() {
|
---|
| 184 | String javaHome = System.getProperty("java.home");
|
---|
| 185 | return javaHome != null && javaHome.contains("openjdk");
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | /**
|
---|
[6850] | 189 | * Get the package name including detailed version.
|
---|
[7314] | 190 | * @param packageNames The possible package names (when a package can have different names on different distributions)
|
---|
[6850] | 191 | * @return The package name and package version if it can be identified, null otherwise
|
---|
[7314] | 192 | * @since 7314
|
---|
[6850] | 193 | */
|
---|
[7314] | 194 | public static String getPackageDetails(String ... packageNames) {
|
---|
[6850] | 195 | try {
|
---|
[7314] | 196 | boolean dpkg = Files.exists(Paths.get("/usr/bin/dpkg-query"));
|
---|
[7349] | 197 | boolean eque = Files.exists(Paths.get("/usr/bin/equery"));
|
---|
[7318] | 198 | boolean rpm = Files.exists(Paths.get("/bin/rpm"));
|
---|
[7349] | 199 | if (dpkg || rpm || eque) {
|
---|
[7314] | 200 | for (String packageName : packageNames) {
|
---|
| 201 | String[] args = null;
|
---|
| 202 | if (dpkg) {
|
---|
| 203 | args = new String[] {"dpkg-query", "--show", "--showformat", "${Architecture}-${Version}", packageName};
|
---|
[7349] | 204 | } else if (eque) {
|
---|
| 205 | args = new String[] {"equery", "-q", "list", "-e", "--format=$fullversion", packageName};
|
---|
[7314] | 206 | } else {
|
---|
| 207 | args = new String[] {"rpm", "-q", "--qf", "%{arch}-%{version}", packageName};
|
---|
| 208 | }
|
---|
| 209 | String version = Utils.execOutput(Arrays.asList(args));
|
---|
[7318] | 210 | if (version != null && !version.contains("not installed")) {
|
---|
[8846] | 211 | return packageName + ':' + version;
|
---|
[7314] | 212 | }
|
---|
| 213 | }
|
---|
[6962] | 214 | }
|
---|
[6850] | 215 | } catch (IOException e) {
|
---|
| 216 | Main.warn(e);
|
---|
| 217 | }
|
---|
[6962] | 218 | return null;
|
---|
[6850] | 219 | }
|
---|
[6920] | 220 |
|
---|
[6850] | 221 | /**
|
---|
[6103] | 222 | * Get the Java package name including detailed version.
|
---|
| 223 | *
|
---|
| 224 | * Some Java bugs are specific to a certain security update, so in addition
|
---|
| 225 | * to the Java version, we also need the exact package version.
|
---|
| 226 | *
|
---|
[7314] | 227 | * @return The package name and package version if it can be identified, null otherwise
|
---|
[6103] | 228 | */
|
---|
| 229 | public String getJavaPackageDetails() {
|
---|
[7318] | 230 | String home = System.getProperty("java.home");
|
---|
[7349] | 231 | if (home.contains("java-7-openjdk") || home.contains("java-1.7.0-openjdk")) {
|
---|
[7318] | 232 | return getPackageDetails("openjdk-7-jre", "java-1_7_0-openjdk", "java-1.7.0-openjdk");
|
---|
[7349] | 233 | } else if (home.contains("icedtea")) {
|
---|
| 234 | return getPackageDetails("icedtea-bin");
|
---|
| 235 | } else if (home.contains("oracle")) {
|
---|
| 236 | return getPackageDetails("oracle-jdk-bin", "oracle-jre-bin");
|
---|
[6103] | 237 | }
|
---|
| 238 | return null;
|
---|
| 239 | }
|
---|
[6920] | 240 |
|
---|
[6850] | 241 | /**
|
---|
| 242 | * Get the Web Start package name including detailed version.
|
---|
| 243 | *
|
---|
[7314] | 244 | * OpenJDK packages are shipped with icedtea-web package,
|
---|
| 245 | * but its version generally does not match main java package version.
|
---|
[6920] | 246 | *
|
---|
[6850] | 247 | * Simply return {@code null} if there's no separate package for Java WebStart.
|
---|
| 248 | *
|
---|
| 249 | * @return The package name and package version if it can be identified, null otherwise
|
---|
| 250 | */
|
---|
| 251 | public String getWebStartPackageDetails() {
|
---|
[7314] | 252 | if (isOpenJDK()) {
|
---|
| 253 | return getPackageDetails("icedtea-netx", "icedtea-web");
|
---|
[6850] | 254 | }
|
---|
| 255 | return null;
|
---|
| 256 | }
|
---|
[6103] | 257 |
|
---|
[5994] | 258 | protected String buildOSDescription() {
|
---|
[5850] | 259 | String osName = System.getProperty("os.name");
|
---|
| 260 | if ("Linux".equalsIgnoreCase(osName)) {
|
---|
| 261 | try {
|
---|
[8540] | 262 | // Try lsb_release (only available on LSB-compliant Linux systems,
|
---|
| 263 | // see https://www.linuxbase.org/lsb-cert/productdir.php?by_prod )
|
---|
[5850] | 264 | Process p = Runtime.getRuntime().exec("lsb_release -ds");
|
---|
[7082] | 265 | try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
|
---|
[7037] | 266 | String line = Utils.strip(input.readLine());
|
---|
| 267 | if (line != null && !line.isEmpty()) {
|
---|
[8510] | 268 | line = line.replaceAll("\"+", "");
|
---|
| 269 | line = line.replaceAll("NAME=", ""); // strange code for some Gentoo's
|
---|
| 270 | if (line.startsWith("Linux ")) // e.g. Linux Mint
|
---|
[7037] | 271 | return line;
|
---|
[8510] | 272 | else if (!line.isEmpty())
|
---|
[7037] | 273 | return "Linux " + line;
|
---|
| 274 | }
|
---|
[5850] | 275 | }
|
---|
| 276 | } catch (IOException e) {
|
---|
[5877] | 277 | // Non LSB-compliant Linux system. List of common fallback release files: http://linuxmafia.com/faq/Admin/release-files.html
|
---|
| 278 | for (LinuxReleaseInfo info : new LinuxReleaseInfo[]{
|
---|
| 279 | new LinuxReleaseInfo("/etc/lsb-release", "DISTRIB_DESCRIPTION", "DISTRIB_ID", "DISTRIB_RELEASE"),
|
---|
| 280 | new LinuxReleaseInfo("/etc/os-release", "PRETTY_NAME", "NAME", "VERSION"),
|
---|
| 281 | new LinuxReleaseInfo("/etc/arch-release"),
|
---|
| 282 | new LinuxReleaseInfo("/etc/debian_version", "Debian GNU/Linux "),
|
---|
| 283 | new LinuxReleaseInfo("/etc/fedora-release"),
|
---|
| 284 | new LinuxReleaseInfo("/etc/gentoo-release"),
|
---|
[7314] | 285 | new LinuxReleaseInfo("/etc/redhat-release"),
|
---|
| 286 | new LinuxReleaseInfo("/etc/SuSE-release")
|
---|
[5877] | 287 | }) {
|
---|
| 288 | String description = info.extractDescription();
|
---|
| 289 | if (description != null && !description.isEmpty()) {
|
---|
| 290 | return "Linux " + description;
|
---|
| 291 | }
|
---|
| 292 | }
|
---|
[5850] | 293 | }
|
---|
| 294 | }
|
---|
| 295 | return osName;
|
---|
| 296 | }
|
---|
[6070] | 297 |
|
---|
[5994] | 298 | @Override
|
---|
| 299 | public String getOSDescription() {
|
---|
| 300 | if (osDescription == null) {
|
---|
| 301 | osDescription = buildOSDescription();
|
---|
| 302 | }
|
---|
| 303 | return osDescription;
|
---|
| 304 | }
|
---|
[6070] | 305 |
|
---|
[5877] | 306 | protected static class LinuxReleaseInfo {
|
---|
| 307 | private final String path;
|
---|
| 308 | private final String descriptionField;
|
---|
| 309 | private final String idField;
|
---|
| 310 | private final String releaseField;
|
---|
| 311 | private final boolean plainText;
|
---|
| 312 | private final String prefix;
|
---|
[6070] | 313 |
|
---|
[5877] | 314 | public LinuxReleaseInfo(String path, String descriptionField, String idField, String releaseField) {
|
---|
| 315 | this(path, descriptionField, idField, releaseField, false, null);
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 | public LinuxReleaseInfo(String path) {
|
---|
| 319 | this(path, null, null, null, true, null);
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | public LinuxReleaseInfo(String path, String prefix) {
|
---|
| 323 | this(path, null, null, null, true, prefix);
|
---|
| 324 | }
|
---|
[6070] | 325 |
|
---|
[5877] | 326 | private LinuxReleaseInfo(String path, String descriptionField, String idField, String releaseField, boolean plainText, String prefix) {
|
---|
| 327 | this.path = path;
|
---|
| 328 | this.descriptionField = descriptionField;
|
---|
| 329 | this.idField = idField;
|
---|
| 330 | this.releaseField = releaseField;
|
---|
| 331 | this.plainText = plainText;
|
---|
| 332 | this.prefix = prefix;
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | @Override public String toString() {
|
---|
[6070] | 336 | return "ReleaseInfo [path=" + path + ", descriptionField=" + descriptionField +
|
---|
[8846] | 337 | ", idField=" + idField + ", releaseField=" + releaseField + ']';
|
---|
[5877] | 338 | }
|
---|
[6070] | 339 |
|
---|
[5877] | 340 | /**
|
---|
| 341 | * Extracts OS detailed information from a Linux release file (/etc/xxx-release)
|
---|
| 342 | * @return The OS detailed information, or {@code null}
|
---|
| 343 | */
|
---|
| 344 | public String extractDescription() {
|
---|
| 345 | String result = null;
|
---|
| 346 | if (path != null) {
|
---|
[7315] | 347 | Path p = Paths.get(path);
|
---|
| 348 | if (Files.exists(p)) {
|
---|
| 349 | try (BufferedReader reader = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
|
---|
[5877] | 350 | String id = null;
|
---|
| 351 | String release = null;
|
---|
| 352 | String line;
|
---|
| 353 | while (result == null && (line = reader.readLine()) != null) {
|
---|
| 354 | if (line.contains("=")) {
|
---|
| 355 | String[] tokens = line.split("=");
|
---|
| 356 | if (tokens.length >= 2) {
|
---|
| 357 | // Description, if available, contains exactly what we need
|
---|
| 358 | if (descriptionField != null && descriptionField.equalsIgnoreCase(tokens[0])) {
|
---|
| 359 | result = Utils.strip(tokens[1]);
|
---|
| 360 | } else if (idField != null && idField.equalsIgnoreCase(tokens[0])) {
|
---|
| 361 | id = Utils.strip(tokens[1]);
|
---|
| 362 | } else if (releaseField != null && releaseField.equalsIgnoreCase(tokens[0])) {
|
---|
| 363 | release = Utils.strip(tokens[1]);
|
---|
| 364 | }
|
---|
| 365 | }
|
---|
| 366 | } else if (plainText && !line.isEmpty()) {
|
---|
| 367 | // Files composed of a single line
|
---|
| 368 | result = Utils.strip(line);
|
---|
| 369 | }
|
---|
| 370 | }
|
---|
| 371 | // If no description has been found, try to rebuild it with "id" + "release" (i.e. "name" + "version")
|
---|
| 372 | if (result == null && id != null && release != null) {
|
---|
[8846] | 373 | result = id + ' ' + release;
|
---|
[5877] | 374 | }
|
---|
| 375 | } catch (IOException e) {
|
---|
| 376 | // Ignore
|
---|
[8513] | 377 | if (Main.isTraceEnabled()) {
|
---|
| 378 | Main.trace(e.getMessage());
|
---|
| 379 | }
|
---|
[5877] | 380 | }
|
---|
| 381 | }
|
---|
| 382 | }
|
---|
| 383 | // Append prefix if any
|
---|
| 384 | if (result != null && !result.isEmpty() && prefix != null && !prefix.isEmpty()) {
|
---|
| 385 | result = prefix + result;
|
---|
| 386 | }
|
---|
[8510] | 387 | if (result != null)
|
---|
| 388 | result = result.replaceAll("\"+", "");
|
---|
[5877] | 389 | return result;
|
---|
| 390 | }
|
---|
| 391 | }
|
---|
[6920] | 392 |
|
---|
[6443] | 393 | protected void askUpdateJava(String version) {
|
---|
[8939] | 394 | if (!GraphicsEnvironment.isHeadless()) {
|
---|
| 395 | askUpdateJava(version, "https://www.java.com/download");
|
---|
| 396 | }
|
---|
[6943] | 397 | }
|
---|
| 398 |
|
---|
[6952] | 399 | protected void askUpdateJava(final String version, final String url) {
|
---|
| 400 | GuiHelper.runInEDTAndWait(new Runnable() {
|
---|
| 401 | @Override
|
---|
| 402 | public void run() {
|
---|
| 403 | ExtendedDialog ed = new ExtendedDialog(
|
---|
| 404 | Main.parent,
|
---|
| 405 | tr("Outdated Java version"),
|
---|
[8934] | 406 | new String[]{tr("OK"), tr("Update Java"), tr("Cancel")});
|
---|
[6952] | 407 | // Check if the dialog has not already been permanently hidden by user
|
---|
[7001] | 408 | if (!ed.toggleEnable("askUpdateJava8").toggleCheckState()) {
|
---|
[8934] | 409 | ed.setButtonIcons(new String[]{"ok", "java", "cancel"}).setCancelButton(3);
|
---|
[6952] | 410 | ed.setMinimumSize(new Dimension(480, 300));
|
---|
| 411 | ed.setIcon(JOptionPane.WARNING_MESSAGE);
|
---|
[8934] | 412 | StringBuilder content = new StringBuilder(tr("You are running version {0} of Java.", "<b>"+version+"</b>"))
|
---|
| 413 | .append("<br><br>");
|
---|
[6952] | 414 | if ("Sun Microsystems Inc.".equals(System.getProperty("java.vendor")) && !isOpenJDK()) {
|
---|
[8934] | 415 | content.append("<b>").append(tr("This version is no longer supported by {0} since {1} and is not recommended for use.",
|
---|
| 416 | "Oracle", tr("April 2015"))).append("</b><br><br>");
|
---|
[6952] | 417 | }
|
---|
[8934] | 418 | content.append("<b>")
|
---|
| 419 | .append(tr("JOSM will soon stop working with this version; we highly recommend you to update to Java {0}.", "8"))
|
---|
| 420 | .append("</b><br><br>")
|
---|
| 421 | .append(tr("Would you like to update now ?"));
|
---|
| 422 | ed.setContent(content.toString());
|
---|
[6920] | 423 |
|
---|
[8934] | 424 | if (ed.showDialog().getValue() == 2) {
|
---|
[6952] | 425 | try {
|
---|
| 426 | openUrl(url);
|
---|
| 427 | } catch (IOException e) {
|
---|
| 428 | Main.warn(e);
|
---|
| 429 | }
|
---|
| 430 | }
|
---|
[6443] | 431 | }
|
---|
| 432 | }
|
---|
[6952] | 433 | });
|
---|
[6443] | 434 | }
|
---|
[7206] | 435 |
|
---|
| 436 | @Override
|
---|
[7343] | 437 | public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
|
---|
[7206] | 438 | throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
|
---|
| 439 | // TODO setup HTTPS certificate on Unix systems
|
---|
[7343] | 440 | return false;
|
---|
[7206] | 441 | }
|
---|
[7829] | 442 |
|
---|
| 443 | @Override
|
---|
| 444 | public File getDefaultCacheDirectory() {
|
---|
[7834] | 445 | return new File(Main.pref.getUserDataDirectory(), "cache");
|
---|
[7829] | 446 | }
|
---|
[7831] | 447 |
|
---|
| 448 | @Override
|
---|
| 449 | public File getDefaultPrefDirectory() {
|
---|
| 450 | return new File(System.getProperty("user.home"), ".josm");
|
---|
| 451 | }
|
---|
[7834] | 452 |
|
---|
| 453 | @Override
|
---|
| 454 | public File getDefaultUserDataDirectory() {
|
---|
| 455 | // Use preferences directory by default
|
---|
| 456 | return Main.pref.getPreferencesDirectory();
|
---|
| 457 | }
|
---|
[8015] | 458 |
|
---|
| 459 | /**
|
---|
[8099] | 460 | * <p>Add more fallback fonts to the Java runtime, in order to get
|
---|
| 461 | * support for more scripts.</p>
|
---|
[8015] | 462 | *
|
---|
[8099] | 463 | * <p>The font configuration in Java doesn't include some Indic scripts,
|
---|
[8518] | 464 | * even though MS Windows ships with fonts that cover these unicode ranges.</p>
|
---|
[8015] | 465 | *
|
---|
[8099] | 466 | * <p>To fix this, the fontconfig.properties template is copied to the JOSM
|
---|
[8015] | 467 | * cache folder. Then, the additional entries are added to the font
|
---|
| 468 | * configuration. Finally the system property "sun.awt.fontconfig" is set
|
---|
[8099] | 469 | * to the customized fontconfig.properties file.</p>
|
---|
[8073] | 470 | *
|
---|
[8518] | 471 | * <p>This is a crude hack, but better than no font display at all for these languages.
|
---|
[8015] | 472 | * There is no guarantee, that the template file
|
---|
| 473 | * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default
|
---|
| 474 | * configuration (which is in a binary format).
|
---|
| 475 | * Furthermore, the system property "sun.awt.fontconfig" is undocumented and
|
---|
[8099] | 476 | * may no longer work in future versions of Java.</p>
|
---|
[8015] | 477 | *
|
---|
[8099] | 478 | * <p>Related Java bug: <a href="https://bugs.openjdk.java.net/browse/JDK-8008572">JDK-8008572</a></p>
|
---|
| 479 | *
|
---|
[8015] | 480 | * @param templateFileName file name of the fontconfig.properties template file
|
---|
| 481 | */
|
---|
| 482 | protected void extendFontconfig(String templateFileName) {
|
---|
| 483 | String customFontconfigFile = Main.pref.get("fontconfig.properties", null);
|
---|
| 484 | if (customFontconfigFile != null) {
|
---|
| 485 | Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile);
|
---|
| 486 | return;
|
---|
| 487 | }
|
---|
| 488 | if (!Main.pref.getBoolean("font.extended-unicode", true))
|
---|
| 489 | return;
|
---|
| 490 |
|
---|
| 491 | String javaLibPath = System.getProperty("java.home") + File.separator + "lib";
|
---|
| 492 | Path templateFile = FileSystems.getDefault().getPath(javaLibPath, templateFileName);
|
---|
| 493 | if (!Files.isReadable(templateFile)) {
|
---|
| 494 | Main.warn("extended font config - unable to find font config template file "+templateFile.toString());
|
---|
| 495 | return;
|
---|
| 496 | }
|
---|
[8129] | 497 | try (FileInputStream fis = new FileInputStream(templateFile.toFile())) {
|
---|
[8015] | 498 | Properties props = new Properties();
|
---|
[8129] | 499 | props.load(fis);
|
---|
[8015] | 500 | byte[] content = Files.readAllBytes(templateFile);
|
---|
| 501 | File cachePath = Main.pref.getCacheDirectory();
|
---|
| 502 | Path fontconfigFile = cachePath.toPath().resolve("fontconfig.properties");
|
---|
| 503 | OutputStream os = Files.newOutputStream(fontconfigFile);
|
---|
| 504 | os.write(content);
|
---|
[8073] | 505 | try (Writer w = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
|
---|
[8015] | 506 | Collection<FontEntry> extrasPref = Main.pref.getListOfStructs(
|
---|
| 507 | "font.extended-unicode.extra-items", getAdditionalFonts(), FontEntry.class);
|
---|
| 508 | Collection<FontEntry> extras = new ArrayList<>();
|
---|
| 509 | w.append("\n\n# Added by JOSM to extend unicode coverage of Java font support:\n\n");
|
---|
| 510 | List<String> allCharSubsets = new ArrayList<>();
|
---|
| 511 | for (FontEntry entry: extrasPref) {
|
---|
| 512 | Collection<String> fontsAvail = getInstalledFonts();
|
---|
[8404] | 513 | if (fontsAvail != null && fontsAvail.contains(entry.file.toUpperCase(Locale.ENGLISH))) {
|
---|
[8015] | 514 | if (!allCharSubsets.contains(entry.charset)) {
|
---|
| 515 | allCharSubsets.add(entry.charset);
|
---|
| 516 | extras.add(entry);
|
---|
| 517 | } else {
|
---|
| 518 | Main.trace("extended font config - already registered font for charset ''{0}'' - skipping ''{1}''",
|
---|
| 519 | entry.charset, entry.name);
|
---|
| 520 | }
|
---|
| 521 | } else {
|
---|
| 522 | Main.trace("extended font config - Font ''{0}'' not found on system - skipping", entry.name);
|
---|
| 523 | }
|
---|
| 524 | }
|
---|
| 525 | for (FontEntry entry: extras) {
|
---|
| 526 | allCharSubsets.add(entry.charset);
|
---|
| 527 | if ("".equals(entry.name)) {
|
---|
| 528 | continue;
|
---|
| 529 | }
|
---|
| 530 | String key = "allfonts." + entry.charset;
|
---|
| 531 | String value = entry.name;
|
---|
| 532 | String prevValue = props.getProperty(key);
|
---|
| 533 | if (prevValue != null && !prevValue.equals(value)) {
|
---|
| 534 | Main.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
|
---|
| 535 | }
|
---|
[8846] | 536 | w.append(key + '=' + value + '\n');
|
---|
[8015] | 537 | }
|
---|
[8390] | 538 | w.append('\n');
|
---|
[8015] | 539 | for (FontEntry entry: extras) {
|
---|
| 540 | if ("".equals(entry.name) || "".equals(entry.file)) {
|
---|
| 541 | continue;
|
---|
| 542 | }
|
---|
[8846] | 543 | String key = "filename." + entry.name.replace(' ', '_');
|
---|
[8015] | 544 | String value = entry.file;
|
---|
| 545 | String prevValue = props.getProperty(key);
|
---|
| 546 | if (prevValue != null && !prevValue.equals(value)) {
|
---|
| 547 | Main.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
|
---|
| 548 | }
|
---|
[8846] | 549 | w.append(key + '=' + value + '\n');
|
---|
[8015] | 550 | }
|
---|
[8390] | 551 | w.append('\n');
|
---|
[8015] | 552 | String fallback = props.getProperty("sequence.fallback");
|
---|
| 553 | if (fallback != null) {
|
---|
[8846] | 554 | w.append("sequence.fallback=" + fallback + ',' + Utils.join(",", allCharSubsets) + '\n');
|
---|
[8015] | 555 | } else {
|
---|
[8846] | 556 | w.append("sequence.fallback=" + Utils.join(",", allCharSubsets) + '\n');
|
---|
[8015] | 557 | }
|
---|
| 558 | }
|
---|
| 559 | Utils.updateSystemProperty("sun.awt.fontconfig", fontconfigFile.toString());
|
---|
| 560 | } catch (IOException ex) {
|
---|
| 561 | Main.error(ex);
|
---|
| 562 | }
|
---|
| 563 | }
|
---|
| 564 |
|
---|
| 565 | /**
|
---|
| 566 | * Get a list of fonts that are installed on the system.
|
---|
| 567 | *
|
---|
| 568 | * Must be done without triggering the Java Font initialization.
|
---|
| 569 | * (See {@link #extendFontconfig(java.lang.String)}, have to set system
|
---|
[8518] | 570 | * property first, which is then read by sun.awt.FontConfiguration upon initialization.)
|
---|
[8015] | 571 | *
|
---|
| 572 | * @return list of file names
|
---|
| 573 | */
|
---|
| 574 | public Collection<String> getInstalledFonts() {
|
---|
| 575 | throw new UnsupportedOperationException();
|
---|
| 576 | }
|
---|
| 577 |
|
---|
| 578 | /**
|
---|
| 579 | * Get default list of additional fonts to add to the configuration.
|
---|
| 580 | *
|
---|
[8518] | 581 | * Java will choose thee first font in the list that can render a certain character.
|
---|
[8015] | 582 | *
|
---|
| 583 | * @return list of FontEntry objects
|
---|
| 584 | */
|
---|
| 585 | public Collection<FontEntry> getAdditionalFonts() {
|
---|
| 586 | throw new UnsupportedOperationException();
|
---|
| 587 | }
|
---|
[1023] | 588 | }
|
---|