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