diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c529d5befe..4e974c7595c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,7 +78,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where an exception was thrown for the user after Ctrl+Z command. [#9737](https://github.com/JabRef/jabref/issues/9737) - We fixed the citation key generation for (`[authors]`, `[authshort]`, `[authorsAlpha]`, `authIniN`, `authEtAl`, `auth.etal`)[https://docs.jabref.org/setup/citationkeypatterns#special-field-markers] to handle `and others` properly. [koppor#626](https://github.com/koppor/jabref/issues/626) - We fixed the Save/save as file type shows BIBTEX_DB instead of "Bibtex library". [#9372](https://github.com/JabRef/jabref/issues/9372) -- We fixed an issue when overwriting the owner was disabled. [#9896](https://github.com/JabRef/jabref/pull/9896) +- We fixed the default main file directory for non-English Linux users. [#8010](https://github.com/JabRef/jabref/issues/8010) - We fixed an issue regarding recording redundant prefixes in search history. [#9685](https://github.com/JabRef/jabref/issues/9685) - We fixed an issue where passing a URL containing a DOI led to a "No entry found" notification. [#9821](https://github.com/JabRef/jabref/issues/9821) - We fixed some minor visual inconsistencies and issues in the preferences dialog. [#9866](https://github.com/JabRef/jabref/pull/9866) diff --git a/docs/decisions/0026-use-jna-to-determine-default-directory.md b/docs/decisions/0026-use-jna-to-determine-default-directory.md index 40b76f03e39..210be28eb59 100644 --- a/docs/decisions/0026-use-jna-to-determine-default-directory.md +++ b/docs/decisions/0026-use-jna-to-determine-default-directory.md @@ -34,8 +34,10 @@ Swing's FileChooser implemented a very decent directory determination algorithm. It thereby uses `sun.awt.shell.ShellFolder`. * Good, because provides best results on most platforms. +* Good, because also supports localization of the folder name. E.g., `~/Dokumente` in Germany. * Bad, because introduces a dependency on Swing and thereby contradicts the second decision driver. -* Bad, because GraalVM's support Swing is experimental +* Bad, because GraalVM's support Swing is experimental. +* Bad, because handles localization only on Windows. ### Use `user.home` @@ -44,25 +46,17 @@ There is `System.getProperty("user.home");`. * Bad, because "The concept of a HOME directory seems to be a bit vague when it comes to Windows". See for details. * Bad, because it does not include `Documents`: As of 2022, `System.getProperty("user.home")` returns `c:\Users\USERNAME` on Windows 10, whereas - `FileSystemView` returns `C:\Users\USERNAME\Documents`, which is the "better" directory + `FileSystemView` returns `C:\Users\USERNAME\Documents`, which is the "better" directory. ### AppDirs > AppDirs is a small java library which provides a path to the platform dependent special folder/directory. -* Good, because already used in JabRef -* Bad, because does not use `Documents` on Windows, but rather `C:\Users\\AppData\\` as basis +* Good, because already used in JabRef. +* Bad, because does not use `Documents` on Windows, but rather `C:\Users\\AppData\\` as basis. ### Java Native Access -* Good, because no additional dependency required, as it is already loaded by AppDirs -* Good, because it is well maintained and widely used -* Good, because it provides direct access to `Documents` and other system variables - -## More Information - -{You might want to provide additional evidence/confidence for the decision outcome here and/or - document the team agreement on the decision and/or - define when this decision when and how the decision should be realized and if/when it should be re-visited and/or - how the decision is validated. - Links to other decisions and resources might here appear as well.} +* Good, because no additional dependency required, as it is already loaded by AppDirs. +* Good, because it is well maintained and widely used. +* Good, because it provides direct access to `Documents` and other system variables. diff --git a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java index 940a68cc5c3..a1994258688 100644 --- a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java @@ -86,9 +86,9 @@ public static void openExternalViewer(BibDatabaseContext databaseContext, } else if (StandardField.EPRINT == field) { IdentifierParser identifierParser = new IdentifierParser(entry); link = identifierParser.parse(StandardField.EPRINT) - .flatMap(Identifier::getExternalURI) - .map(URI::toASCIIString) - .orElse(link); + .flatMap(Identifier::getExternalURI) + .map(URI::toASCIIString) + .orElse(link); if (Objects.equals(link, initialLink)) { Optional eprintTypeOpt = entry.getField(StandardField.EPRINTTYPE); @@ -127,15 +127,15 @@ private static void openDoi(String doi) throws IOException { } public static void openCustomDoi(String link, PreferencesService preferences, DialogService dialogService) { - DOI.parse(link) - .flatMap(doi -> doi.getExternalURIWithCustomBase(preferences.getDOIPreferences().getDefaultBaseURI())) - .ifPresent(uri -> { - try { - JabRefDesktop.openBrowser(uri); - } catch (IOException e) { - dialogService.showErrorDialogAndWait(Localization.lang("Unable to open link."), e); - } - }); + DOI.parse(link) + .flatMap(doi -> doi.getExternalURIWithCustomBase(preferences.getDOIPreferences().getDefaultBaseURI())) + .ifPresent(uri -> { + try { + JabRefDesktop.openBrowser(uri); + } catch (IOException e) { + dialogService.showErrorDialogAndWait(Localization.lang("Unable to open link."), e); + } + }); } /** diff --git a/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java b/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java index 30f651bbbbd..9f0996a3480 100644 --- a/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java @@ -50,9 +50,4 @@ public String detectProgramPath(String programName, String directoryName) { public Path getApplicationDirectory() { return getUserDirectory(); } - - @Override - public Path getDefaultFileChooserDirectory() { - return Path.of(System.getProperty("user.home")); - } } diff --git a/src/main/java/org/jabref/gui/desktop/os/Linux.java b/src/main/java/org/jabref/gui/desktop/os/Linux.java index a81a81bca55..857f5e24b01 100644 --- a/src/main/java/org/jabref/gui/desktop/os/Linux.java +++ b/src/main/java/org/jabref/gui/desktop/os/Linux.java @@ -5,10 +5,11 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Locale; -import java.util.Objects; import java.util.Optional; import org.jabref.architecture.AllowedToUseAwt; @@ -175,9 +176,34 @@ public Path getApplicationDirectory() { @Override public Path getDefaultFileChooserDirectory() { - return Path.of(Objects.requireNonNullElse( - System.getenv("XDG_DOCUMENTS_DIR"), - System.getProperty("user.home") + "/Documents") - ); + String xdgDocumentsDir = System.getenv("XDG_DOCUMENTS_DIR"); + if (xdgDocumentsDir != null) { + return Path.of(xdgDocumentsDir); + } + + // Make use of xdg-user-dirs + // See https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ for details + try { + Process process = new ProcessBuilder("xdg-user-dir", "DOCUMENTS").start(); // Package name with 's', command without + List strings = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)) + .lines().toList(); + if (strings.isEmpty()) { + LoggerFactory.getLogger(Linux.class).error("xdg-user-dir returned nothing"); + return getUserDirectory(); + } + String documentsDirectory = strings.get(0); + Path documentsPath = Path.of(documentsDirectory); + if (!Files.exists(documentsPath)) { + LoggerFactory.getLogger(Linux.class).error("xdg-user-dir returned non-existant directory {}", documentsDirectory); + return getUserDirectory(); + } + LoggerFactory.getLogger(Linux.class).debug("Got documents path {}", documentsPath); + return documentsPath; + } catch (IOException e) { + LoggerFactory.getLogger(Linux.class).error("Error while executing xdg-user-dir", e); + } + + // Fallback + return getUserDirectory(); } } diff --git a/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java b/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java index a412b489de1..7c3441a24c4 100644 --- a/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import org.jabref.gui.DialogService; @@ -41,7 +42,14 @@ public interface NativeDesktop { * * @return The path to the directory */ - Path getDefaultFileChooserDirectory(); + default Path getDefaultFileChooserDirectory() { + Path userDirectory = getUserDirectory(); + Path documents = userDirectory.resolve("Documents"); + if (!Files.exists(documents)) { + return userDirectory; + } + return documents; + } /** * Returns the path to the system's user directory. diff --git a/src/main/java/org/jabref/gui/desktop/os/OSX.java b/src/main/java/org/jabref/gui/desktop/os/OSX.java index ccd0ca56f92..de85b103eb6 100644 --- a/src/main/java/org/jabref/gui/desktop/os/OSX.java +++ b/src/main/java/org/jabref/gui/desktop/os/OSX.java @@ -59,9 +59,4 @@ public String detectProgramPath(String programName, String directoryName) { public Path getApplicationDirectory() { return Path.of("/Applications"); } - - @Override - public Path getDefaultFileChooserDirectory() { - return Path.of(System.getProperty("user.home") + "/Documents"); - } }