From cb5f9b8b389e5f1cee2e21f646f5773d9117041b Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Sun, 17 Mar 2024 09:55:05 +0900 Subject: [PATCH 01/12] fix: integ-test failed with no X11 screen error (#986) Integration Test can failed with No X11 DISPLAY variable set error. It is because Integ-Test uses ProjectFactory.loadProject but it does not use `TestRealProject` overrides then it use `RealProject#mergeTmx` instead of `TestRealProject#mergeTmx` which is modified to run without X11 screen. The stack trace is like as follows TF_LOAD_ERROR java.awt.HeadlessException: No X11 DISPLAY variable was set, but this program performed an operation which requires it. at java.desktop/java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:166) at java.desktop/java.awt.Window.init(Window.java:501) at java.desktop/java.awt.Window.(Window.java:453) at java.desktop/java.awt.Window.(Window.java:608) at java.desktop/java.awt.Dialog.(Dialog.java:675) at java.desktop/javax.swing.JDialog.(JDialog.java:593) at org.madlonkay.supertmxmerge.gui.MergeWindow.newAsDialog(MergeWindow.java:61) at org.madlonkay.supertmxmerge.MergeController$1.resolve(MergeController.java:98) aat org.madlonkay.supertmxmerge.MergeController.resolve(MergeController.java:232) aat org.madlonkay.supertmxmerge.SuperTmxMerge.merge(SuperTmxMerge.java:86) aat org.omegat.core.data.RealProject.mergeTMX(RealProject.java:1173) aat org.omegat.core.data.RealProject$1.rebaseAndSave(RealProject.java:1050) aat org.omegat.core.team2.RebaseAndCommit.rebaseAndCommit(RebaseAndCommit.java:178) aat org.omegat.core.data.RealProject.rebaseAndCommitProject(RealProject.java:1029) aat org.omegat.core.data.RealProject.loadProject(RealProject.java:383) aat org.omegat.core.data.ProjectFactory.loadProject(ProjectFactory.java:72) aat org.omegat.core.data.TestTeamIntegrationChild.main(TestTeamIntegrationChild.java:213) Signed-off-by: Hiroshi Miura --- .../core/data/TestTeamIntegrationChild.java | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/test-integration/src/org/omegat/core/data/TestTeamIntegrationChild.java b/test-integration/src/org/omegat/core/data/TestTeamIntegrationChild.java index 15132254b6..aeada09dd3 100644 --- a/test-integration/src/org/omegat/core/data/TestTeamIntegrationChild.java +++ b/test-integration/src/org/omegat/core/data/TestTeamIntegrationChild.java @@ -160,18 +160,9 @@ public static void main(String[] args) throws Exception { setRootGitRepositoryMapping(projectProperties.getRepositories(), repo); } projectProperties.autocreateDirectories(); - Core.getAutoSave().disable(); - RealProject p = new TestRealProject(projectProperties); - Core.setProject(p); + Core.setProject(new NotLoadedProject()); glossaryManager = new GlossaryManager(new TestGlossaryTextArea()); - // load project - p.loadProject(true); - if (p.isProjectLoaded()) { - Core.getAutoSave().enable(); - CoreEvents.fireProjectChange(IProjectEventListener.PROJECT_CHANGE_TYPE.LOAD); - } else { - throw new Exception("Project can't be loaded"); - } + loadProject(projectProperties); key = new EntryKey[segCount]; ste = new SourceTextEntry[segCount]; @@ -210,7 +201,7 @@ public static void main(String[] args) throws Exception { }); // load again and check - ProjectFactory.loadProject(projectProperties, true); + loadProject(projectProperties); checkAll(); checkGlossaryEntries(); @@ -225,6 +216,25 @@ public static void main(String[] args) throws Exception { } } + /** + * replacement of ProjectFactory.loadProject for test. + * @param projectProperties + * @throws Exception + */ + static void loadProject(ProjectProperties projectProperties) throws Exception { + Core.getAutoSave().disable(); + RealProject p = new TestRealProject(projectProperties); + Core.setProject(p); + // load project + p.loadProject(true); + if (p.isProjectLoaded()) { + Core.getAutoSave().enable(); + CoreEvents.fireProjectChange(IProjectEventListener.PROJECT_CHANGE_TYPE.LOAD); + } else { + throw new Exception("Project can't be loaded"); + } + } + static void checkRepoUrl(ProjectProperties prop) { for (RepositoryDefinition repository : prop.getRepositories()) { if (repository.getUrl().equals(repo)) { From 2674fa74307ff4210733689c4c62e38886253d66 Mon Sep 17 00:00:00 2001 From: Marc Riera Date: Sun, 17 Mar 2024 02:43:45 +0100 Subject: [PATCH 02/12] feat: send whole locale to Apertium MT (#985) --- .../machinetranslators/apertium/ApertiumTranslate.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/machinetranslators/apertium/src/main/java/org/omegat/machinetranslators/apertium/ApertiumTranslate.java b/machinetranslators/apertium/src/main/java/org/omegat/machinetranslators/apertium/ApertiumTranslate.java index 774a905b0c..7b655988fe 100644 --- a/machinetranslators/apertium/src/main/java/org/omegat/machinetranslators/apertium/ApertiumTranslate.java +++ b/machinetranslators/apertium/src/main/java/org/omegat/machinetranslators/apertium/ApertiumTranslate.java @@ -120,12 +120,12 @@ private String apertiumCode(Language language) { String locale = language.getLocaleCode(); if (!StringUtil.isEmpty(language.getCountryCode())) { - if (locale.equalsIgnoreCase("en_us") || locale.equalsIgnoreCase("pt_br")) { - return locale; // We need en_US and pt_BR - } else if (locale.equalsIgnoreCase("oc_ar")) { - return "oc_aran"; + if (locale.equalsIgnoreCase("oc_ar")) { + return "oci_aran"; } else if (locale.equalsIgnoreCase("ca_va")) { - return "ca_valencia"; + return "cat_valencia"; + } else { + return locale; } } From ef28312f2d3b743abee4a360a6b30aa0e63fd292 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:14:28 +0900 Subject: [PATCH 03/12] chore(deps): bump org.apache.pdfbox:pdfbox from 3.0.1 to 3.0.2 (#982) Bumps org.apache.pdfbox:pdfbox from 3.0.1 to 3.0.2. --- updated-dependencies: - dependency-name: org.apache.pdfbox:pdfbox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 763b98155c..b2d3412ccb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ htmlparser = "1.6-20230203" gnudiff = "1.15" desktopsupport = "0.6.0" protocolhandler = "0.1.4" -pdfbox = "3.0.1" +pdfbox = "3.0.2" maligna = "3.0.1" trie4j = "0.9.10_1" dsl4j = "0.7.1" From e3271af183526b93756b3333de37705ad803f648 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Wed, 20 Mar 2024 20:18:01 +0900 Subject: [PATCH 04/12] fix: classNotFound if menuUI preference is empty Signed-off-by: Hiroshi Miura --- src/org/omegat/util/gui/UIDesignManager.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/org/omegat/util/gui/UIDesignManager.java b/src/org/omegat/util/gui/UIDesignManager.java index d57c5f1c64..c4ce45f017 100644 --- a/src/org/omegat/util/gui/UIDesignManager.java +++ b/src/org/omegat/util/gui/UIDesignManager.java @@ -53,6 +53,7 @@ import org.omegat.util.OStrings; import org.omegat.util.Platform; import org.omegat.util.Preferences; +import org.omegat.util.StringUtil; import org.omegat.util.gui.laf.SystemDarkThemeDetector; import com.vlsolutions.swing.docking.AutoHidePolicy; @@ -99,6 +100,9 @@ public static List getMenuUIPreferences() { } private static void setMenuUI(String menuUIPrefClassName) { + if (StringUtil.isEmpty(menuUIPrefClassName)) { + return; + } try { ClassLoader classLoader = getClassLoader(); Class prefClazz = classLoader.loadClass(menuUIPrefClassName); @@ -197,13 +201,10 @@ public static void initialize() throws IOException { } setTheme(theme); - String menuUI = Preferences.getPreference(Preferences.MENUUI_CLASS_NAME); - if (menuUI != null) { - setMenuUI(menuUI); - } + setMenuUI(Preferences.getPreference(Preferences.MENUUI_CLASS_NAME)); if (UIManager.getColor("OmegaT.source") == null) { - // Theme apparently did not load default colors so we do so now + // Theme apparently did not load default colors, so we do so now loadDefaultColors(UIManager.getDefaults()); } From 7ab446417614293f86eab34d7923be434c28d11c Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Wed, 20 Mar 2024 20:27:50 +0900 Subject: [PATCH 05/12] docs: Update changes.txt - Add BUGS#1250 Signed-off-by: Hiroshi Miura --- release/changes.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/release/changes.txt b/release/changes.txt index e716eb4055..634145d654 100644 --- a/release/changes.txt +++ b/release/changes.txt @@ -2,7 +2,7 @@ OmegaT 6.1.0 ---------------------------------------------------------------------- 44 Enhancement - 67 Bug fixes + 68 Bug fixes 5 Localisation updates ---------------------------------------------------------------------- 6.1.0 vs 6.0.0 @@ -136,6 +136,9 @@ Bug fixes: + - Short glossary items are not recognized with Korean source + https://sourceforge.net/p/omegat/bugs/1250/ + - Debug log in some classes are broken https://sourceforge.net/p/omegat/bugs/1249/ From 362da2ff9048a481f705b4ff2b7436ae453ea89c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 01:10:25 +0900 Subject: [PATCH 06/12] chore(deps): bump groovy from 4.0.19 to 4.0.20 (#981) Bumps `groovy` from 4.0.19 to 4.0.20. Updates `org.apache.groovy:groovy-jsr223` from 4.0.19 to 4.0.20 - [Commits](https://github.com/apache/groovy/commits) Updates `org.apache.groovy:groovy-dateutil` from 4.0.19 to 4.0.20 - [Commits](https://github.com/apache/groovy/commits) Updates `org.apache.groovy:groovy-json` from 4.0.19 to 4.0.20 - [Commits](https://github.com/apache/groovy/commits) Updates `org.apache.groovy:groovy-xml` from 4.0.19 to 4.0.20 - [Commits](https://github.com/apache/groovy/commits) Updates `org.apache.groovy:groovy-swing` from 4.0.19 to 4.0.20 - [Commits](https://github.com/apache/groovy/commits) Updates `org.apache.groovy:groovy-templates` from 4.0.19 to 4.0.20 - [Commits](https://github.com/apache/groovy/commits) --- updated-dependencies: - dependency-name: org.apache.groovy:groovy-jsr223 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.groovy:groovy-dateutil dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.groovy:groovy-json dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.groovy:groovy-xml dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.groovy:groovy-swing dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.groovy:groovy-templates dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b2d3412ccb..578f09691f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] slf4j = { strictly = "[2.0.7, 2.1[", prefer = "2.0.7" } slf4j-format = "0.5.1" -groovy = "4.0.19" +groovy = "4.0.20" junit = "4.13.2" commons_lang3 = "3.14.0" commons_io = "2.15.1" From 6398ea534f717dc478927a25f3a28566a04dc173 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Thu, 21 Mar 2024 01:12:34 +0900 Subject: [PATCH 07/12] refactor: Introduce IProjectFilesList interface (#974) * feat: add IProjectFileList API - Allow test code, script and modules to handle ProjectFileList UI parts - Add default constructor for ProjectFilesListController class Signed-off-by: Hiroshi Miura * refactor: remove projWin from MainWindow class - Add Core.getProjectFilesList function - Create projWin window in Core.initializeGUI - Reduce MainWindow object direct access w/o IMainWindow API from MenuHandler Signed-off-by: Hiroshi Miura --------- Signed-off-by: Hiroshi Miura --- src/org/omegat/core/Core.java | 10 +++++- .../gui/filelist/IProjectFilesList.java | 33 +++++++++++++++++++ .../filelist/ProjectFilesListController.java | 9 +++-- src/org/omegat/gui/main/MainWindow.java | 5 --- .../gui/main/MainWindowMenuHandler.java | 8 +++-- src/org/omegat/gui/main/MainWindowUI.java | 11 ++----- 6 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 src/org/omegat/gui/filelist/IProjectFilesList.java diff --git a/src/org/omegat/core/Core.java b/src/org/omegat/core/Core.java index 87a31fee63..4b69f230cb 100644 --- a/src/org/omegat/core/Core.java +++ b/src/org/omegat/core/Core.java @@ -58,6 +58,8 @@ import org.omegat.gui.editor.mark.IMarker; import org.omegat.gui.exttrans.IMachineTranslation; import org.omegat.gui.exttrans.MachineTranslateTextArea; +import org.omegat.gui.filelist.IProjectFilesList; +import org.omegat.gui.filelist.ProjectFilesListController; import org.omegat.gui.glossary.GlossaryManager; import org.omegat.gui.glossary.GlossaryTextArea; import org.omegat.gui.glossary.IGlossaries; @@ -103,6 +105,7 @@ private Core() { private static IIssues issuesWindow; private static IMatcher matcher; private static FilterMaster filterMaster; + private static IProjectFilesList projWin; protected static IAutoSave saveThread; private static final ReentrantLock EXCLUSIVE_RUN_LOCK = new ReentrantLock(); @@ -172,6 +175,10 @@ public static void setFilterMaster(FilterMaster newFilterMaster) { EntryKey.setIgnoreFileContext(newFilterMaster.getConfig().isIgnoreFileContext()); } + public static IProjectFilesList getProjectFilesList() { + return projWin; + } + public static MachineTranslateTextArea getMachineTranslatePane() { return machineTranslatePane; } @@ -255,7 +262,7 @@ public static void initializeGUI(final Map params) throws Except // 4. Initialize other components. They add themselves to the main window. editor = new EditorController(me); tagValidation = new TagValidationTool(); - issuesWindow = new IssuesPanelController(me); + issuesWindow = new IssuesPanelController(me.getApplicationFrame()); matcher = new MatchesTextArea(me); GlossaryTextArea glossaryArea = new GlossaryTextArea(me); glossary = glossaryArea; @@ -266,6 +273,7 @@ public static void initializeGUI(final Map params) throws Except dictionaries = new DictionariesTextArea(me); multiple = new MultipleTransPane(me); new SegmentPropertiesArea(me); + projWin = new ProjectFilesListController(); SaveThread th = new SaveThread(); saveThread = th; diff --git a/src/org/omegat/gui/filelist/IProjectFilesList.java b/src/org/omegat/gui/filelist/IProjectFilesList.java new file mode 100644 index 0000000000..076d342518 --- /dev/null +++ b/src/org/omegat/gui/filelist/IProjectFilesList.java @@ -0,0 +1,33 @@ +/************************************************************************** + OmegaT - Computer Assisted Translation (CAT) tool + with fuzzy matching, translation memory, keyword search, + glossaries, and translation leveraging into updated projects. + + Copyright (C) 2024 Hiroshi Miura + Home page: https://www.omegat.org/ + Support center: https://omegat.org/support + + This file is part of OmegaT. + + OmegaT is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OmegaT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **************************************************************************/ + +package org.omegat.gui.filelist; + +public interface IProjectFilesList { + + boolean isActive(); + + void setActive(boolean b); +} diff --git a/src/org/omegat/gui/filelist/ProjectFilesListController.java b/src/org/omegat/gui/filelist/ProjectFilesListController.java index 9fb008640d..a3c4c83d87 100644 --- a/src/org/omegat/gui/filelist/ProjectFilesListController.java +++ b/src/org/omegat/gui/filelist/ProjectFilesListController.java @@ -130,7 +130,7 @@ * @author Aaron Madlon-Kay */ @SuppressWarnings("serial") -public class ProjectFilesListController { +public class ProjectFilesListController implements IProjectFilesList { private ProjectFilesList list; private FileInfoModel modelFiles; @@ -139,9 +139,13 @@ public class ProjectFilesListController { private TableFilterPanel filterPanel; - private Font defaultFont; + private final Font defaultFont; public ProjectFilesListController(MainWindow parent) { + this(); + } + + public ProjectFilesListController() { list = new ProjectFilesList(); @@ -679,6 +683,7 @@ private void propagateTableColumns() { } } + enum FilesTableColumn { FILE_NAME(0, OStrings.getString("PF_FILENAME"), String.class, new DataTableStyling.PatternHighlightRenderer(false)), diff --git a/src/org/omegat/gui/main/MainWindow.java b/src/org/omegat/gui/main/MainWindow.java index 64943c83de..5f2e8a1c2b 100644 --- a/src/org/omegat/gui/main/MainWindow.java +++ b/src/org/omegat/gui/main/MainWindow.java @@ -75,7 +75,6 @@ import org.omegat.core.events.IApplicationEventListener; import org.omegat.core.events.IProjectEventListener; import org.omegat.core.matching.NearString; -import org.omegat.gui.filelist.ProjectFilesListController; import org.omegat.gui.matches.IMatcher; import org.omegat.gui.search.SearchWindowController; import org.omegat.util.OStrings; @@ -115,8 +114,6 @@ public class MainWindow extends JFrame implements IMainWindow { public final BaseMainWindowMenu menu; - protected ProjectFilesListController projWin; - /** * The font for main window (source and target text) and for match and * glossary windows @@ -184,8 +181,6 @@ public void windowDeactivated(WindowEvent we) { // load default font from preferences font = FontUtil.getScaledFont(); - MainWindowUI.createMainComponents(this, font); - getContentPane().add(MainWindowUI.initDocking(this), BorderLayout.CENTER); pack(); getContentPane().add(MainWindowUI.createStatusBar(this), BorderLayout.SOUTH); diff --git a/src/org/omegat/gui/main/MainWindowMenuHandler.java b/src/org/omegat/gui/main/MainWindowMenuHandler.java index b1e70ca3e5..dea8af13a8 100644 --- a/src/org/omegat/gui/main/MainWindowMenuHandler.java +++ b/src/org/omegat/gui/main/MainWindowMenuHandler.java @@ -71,6 +71,7 @@ import org.omegat.gui.editor.IEditor; import org.omegat.gui.editor.SegmentExportImport; import org.omegat.gui.exttrans.MachineTranslationInfo; +import org.omegat.gui.filelist.IProjectFilesList; import org.omegat.gui.filters2.FiltersCustomizerController; import org.omegat.gui.issues.IssueProvidersSelectorController; import org.omegat.gui.preferences.PreferencesWindowController; @@ -187,7 +188,7 @@ public void projectSaveMenuItemActionPerformed() { */ public void projectCompileMenuItemActionPerformed() { if (!checkTags()) { - return; + return; } ProjectUICommands.projectCompile(); @@ -250,12 +251,13 @@ public void projectEditMenuItemActionPerformed() { } public void viewFileListMenuItemActionPerformed() { - if (mainWindow.projWin == null) { + IProjectFilesList projWin = Core.getProjectFilesList(); + if (projWin == null) { mainWindow.menu.viewFileListMenuItem.setSelected(false); return; } - mainWindow.projWin.setActive(!mainWindow.projWin.isActive()); + projWin.setActive(!projWin.isActive()); } public void projectAccessRootMenuItemActionPerformed() { diff --git a/src/org/omegat/gui/main/MainWindowUI.java b/src/org/omegat/gui/main/MainWindowUI.java index bbcab96ecf..6adaf2d406 100644 --- a/src/org/omegat/gui/main/MainWindowUI.java +++ b/src/org/omegat/gui/main/MainWindowUI.java @@ -50,19 +50,19 @@ import javax.swing.UIManager; import javax.swing.border.Border; +import org.openide.awt.Mnemonics; + import org.omegat.core.Core; import org.omegat.core.CoreEvents; import org.omegat.core.events.IApplicationEventListener; import org.omegat.core.events.IProjectEventListener; import org.omegat.gui.editor.EditorController; -import org.omegat.gui.filelist.ProjectFilesListController; import org.omegat.util.Log; import org.omegat.util.OConsts; import org.omegat.util.OStrings; import org.omegat.util.Preferences; import org.omegat.util.StaticUtils; import org.omegat.util.gui.UIDesignManager; -import org.openide.awt.Mnemonics; import com.vlsolutions.swing.docking.DockingDesktop; import com.vlsolutions.swing.docking.event.DockableStateWillChangeEvent; @@ -93,13 +93,6 @@ public enum StatusBarMode { DEFAULT, PERCENTAGE, }; - /** - * Create main UI panels. - */ - public static void createMainComponents(final MainWindow mainWindow, final Font font) { - mainWindow.projWin = new ProjectFilesListController(mainWindow); - } - /** * Create docking desktop panel. */ From 7e92d1117b8c4b02c1d646ff1fd836981e47485f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 01:16:09 +0900 Subject: [PATCH 08/12] chore(deps): bump com.formdev:flatlaf from 3.3 to 3.4 (#965) Bumps [com.formdev:flatlaf](https://github.com/JFormDesigner/FlatLaf) from 3.3 to 3.4. - [Release notes](https://github.com/JFormDesigner/FlatLaf/releases) - [Changelog](https://github.com/JFormDesigner/FlatLaf/blob/main/CHANGELOG.md) - [Commits](https://github.com/JFormDesigner/FlatLaf/compare/3.3...3.4) --- updated-dependencies: - dependency-name: com.formdev:flatlaf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 578f09691f..85a3ea0d66 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,7 +48,7 @@ xjc = "2.3.4" jna = "5.13.0" jfa = "1.2.0" tipoftheday = "0.4.4" -flatlaf="3.3" +flatlaf="3.4" [libraries] slf4j-api = {group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j"} From 84808da3a13a48b571f31f56f7222597b89cba66 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Thu, 21 Mar 2024 01:18:03 +0900 Subject: [PATCH 09/12] [BUGS#1235] fix: hardening plugin installer utility class (#841) * fix: hardening plugin installer utility class - Refactor PluginUtils#loadPlugins to register "jarFile!className" URL even when it is main jar file. - Check nullity of `currentInfo.getUrl()` and skip retrieving of jarFile object when null. - Check jar file path that is not under installDir system directory, when try to remove it. Signed-off-by: Hiroshi Miura * refactor: check a current plugin in a system folder - Apply spotless for PluginInstaller.java - compare installDir with jarFile through NIO2 path object Signed-off-by: Hiroshi Miura * Update src/org/omegat/util/PluginInstaller.java * fix: dynamic class loader on Java9 or later PluginInstaller#parsePluginJarFileManifest method depends on classloader instrument external jar file manifest entries. It is impossible with standard URLClassLoader in Java9 and later, we have already had a custom class loader MainClassLoader class but PluginInstaller don't use it. This fix is to use MainClassLoader instead of URLClassLoader. We also need to change MainClassLoader#addJarToClasspath method to be public. Signed-off-by: Hiroshi Miura * feat: show message when plugin installation - Change message when override and upgrade - Show message after succeeded - refine confirmation messages Signed-off-by: Hiroshi Miura * fix: pluginInstaller parser to return correct information Signed-off-by: Hiroshi Miura * reafactor: MainClassLoader#addJarToClasspath to accept URL or File object Signed-off-by: Hiroshi Miura * reafactor: MainClassLoader#appendToClassPathForInstrumentation to use addJarToClasspath method Signed-off-by: Hiroshi Miura * chore: Update plugin installation dialog titles and messages * chore: Update key name Apply the change from `PREFS_PLUGINS_CONFIRM_OVERRIDE` to `PREFS_PLUGINS_CONFIRM_OVERWRITE` made in `Bundle.properties` to the code. * chore: Add key for plugin upgrade dialog title * chore: Update plugin installation/upgrade dialog titles and messages * docs: description of plugin installer - Explain plugin install button. An intaller feature accept jar file and a zip archive which contents include a plugin jar file and extra files such as README, LICENSE.txt etc. - Also add warning that OmegaT does not accept a file which is originally jar file but renamed with .zip file extension. - Tweak dialog message to clarify it. Signed-off-by: Hiroshi Miura * feat: show table when plugin has multiple entries Signed-off-by: Hiroshi Miura * style: redundant public modifier Signed-off-by: Hiroshi Miura * feat: change UI to reduce current plugin name Signed-off-by: Hiroshi Miura * feat: localize table header Signed-off-by: Hiroshi Miura * chore: Update plugin installation/upgrade dialog titles and messages * chore: Update plugin file documentation * chore: Update dialog and plugin file documentation --------- Signed-off-by: Hiroshi Miura Co-authored-by: kazephil --- doc_src/en/OmegaT_Preferences.xml | 12 +- src/org/omegat/Bundle.properties | 19 +- src/org/omegat/Main.java | 10 +- src/org/omegat/MainClassLoader.java | 33 ++- .../omegat/filters2/master/PluginUtils.java | 15 +- src/org/omegat/util/PluginInstaller.java | 218 ++++++++++++++---- 6 files changed, 229 insertions(+), 78 deletions(-) diff --git a/doc_src/en/OmegaT_Preferences.xml b/doc_src/en/OmegaT_Preferences.xml index f3911f07dd..e5c9390a29 100644 --- a/doc_src/en/OmegaT_Preferences.xml +++ b/doc_src/en/OmegaT_Preferences.xml @@ -1704,8 +1704,18 @@ ${filePath}> linkend="application.folder">application folder. Additional plugins can be found on the OmegaT development + url="https://sourceforge.net/p/omegat/wiki/Plugins/">OmegaT wiki site. + Use the Install plugin from disk button to install or upgrade your + plugin. Plugin files are JAR files with a .jar extension. + OmegaT also accepts zip archives that contain a JAR plugin file. + Third party plugins are typically distributed as zip archives that contain the plugin file + itself, as well as README and LICENSE files. + + + The plugin installer only accepts archives containing a JAR plugin. + It does not recognize plugin files renamed with a .zip extension. +
diff --git a/src/org/omegat/Bundle.properties b/src/org/omegat/Bundle.properties index 977fbf6a87..da4e848fb9 100644 --- a/src/org/omegat/Bundle.properties +++ b/src/org/omegat/Bundle.properties @@ -2137,7 +2137,7 @@ GUI_DICTIONARY_INSTALLER_AVAILABLE=Available languages: # org.omegat.gui.dialogs.PluginInstallerDialog GUI_PLUGIN_INSTALLER_TITLE=Plugin Installer -GUI_PLUGIN_OPEN=Select a plugin file (*.jar, *.zip) +GUI_PLUGIN_OPEN=Select a plugin file (*.jar) or a zip archive (*.zip) that contains a plugin jar file. GUI_PLUGIN_INSTALLER_INSTALL=Install # matches text area popup menu @@ -2910,13 +2910,20 @@ PREFS_PLUGINS_COL_VERSION=Version PREFS_PLUGINS_COL_AUTHOR=Author PREFS_PLUGINS_COL_DESCRIPTION=Description PREFS_PLUGINS_INSTALL_FROM_DISK=&Install plugin from disk -PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION=Install plugin file -PREFS_PLUGINS_CONFIRM_UPGRADE=Upgrade/override plugin {0}\n\ - current: {1}\n\ - new version: {2} -PREFS_PLUGINS_CONFIRM_INSTALL=Install plugin {0} {1} +PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION=Install Plugin +PREFS_PLUGINS_TITLE_CONFIRM_UPGRADE=Upgrade Plugin +PREFS_PLUGINS_CONFIRM_UPGRADE=Upgrade the {0} plugin?\n\ + Current version: {1}\n\ + New version: {2} +PREFS_PLUGINS_CONFIRM_OVERWRITE=Version {1} is already installed.\n\ + Reinstall the {0} plugin?\n +PREFS_PLUGINS_CONFIRM_INSTALL=Install the {0} {1} plugin? PREFS_PLUGINS_INSTALLATION_FAILED=Plugin installation failed. +PREFS_PLUGINS_INSTALLATION_SUCCEED=Plugin installation successful. Restart OmegaT to activate it. PREFS_PLUGINS_UNKNOWN_ARCHIVE=This file is not a plugin archive. +PREFS_PLUGINS_TITLE_NAME=Plugin Name +PREFS_PLUGINS_TITLE_CURRENT_VERSION=Installed +PREFS_PLUGINS_TITLE_TARGET_VERSION=Selected # PREFERENCES - UPDATES VERSION_CHECK_UP_TO_DATE=You already have the latest version. diff --git a/src/org/omegat/Main.java b/src/org/omegat/Main.java index a3bbe12e8d..101a30e01e 100644 --- a/src/org/omegat/Main.java +++ b/src/org/omegat/Main.java @@ -325,9 +325,13 @@ private static void applyConfigFile(String path) { */ protected static int runGUI() { ClassLoader cl = ClassLoader.getSystemClassLoader(); - MainClassLoader mainClassLoader = (cl instanceof MainClassLoader) ? (MainClassLoader) cl - : new MainClassLoader(cl); - PluginUtils.getThemePluginJars().forEach(mainClassLoader::add); + MainClassLoader mainClassLoader; + if (cl instanceof MainClassLoader) { + mainClassLoader = (MainClassLoader) cl; + } else { + mainClassLoader = new MainClassLoader(cl); + } + PluginUtils.getThemePluginJars().forEach(mainClassLoader::addJarToClasspath); UIManager.put("ClassLoader", mainClassLoader); // macOS-specific - they must be set BEFORE any GUI calls diff --git a/src/org/omegat/MainClassLoader.java b/src/org/omegat/MainClassLoader.java index 7d9885fb8f..2fabec86b6 100644 --- a/src/org/omegat/MainClassLoader.java +++ b/src/org/omegat/MainClassLoader.java @@ -42,20 +42,9 @@ public final class MainClassLoader extends URLClassLoader { registerAsParallelCapable(); } - private String name; - - /** - * Java9 compatible class loader constructor. - * @param name name of class loader - * @param parent - */ - public MainClassLoader(String name, ClassLoader parent) { - this(parent); - this.name = name; - } - /* - * Required when this classloader is used as the system classloader + * Java9 compatible class loader constructor. + * @param parent system class loader. */ public MainClassLoader(ClassLoader parent) { this(new URL[0], parent); @@ -70,16 +59,20 @@ public MainClassLoader() { } /** - * Main class can add jar classpath for plugins in dynamic manner. - * @param url + * Add URL to classpath. + * @param url to added. */ - synchronized void add(URL url) { + public void addJarToClasspath(URL url) { addURL(url); } - synchronized void addJarToClasspath(String jarName) - throws MalformedURLException { - URL url = new File(jarName).toURI().toURL(); + /** + * Add Jar file into classpath. + * @param jarFile JAR file to add to classpath. + * @throws MalformedURLException when a malformed File object is passed. + */ + public void addJarToClasspath(File jarFile) throws MalformedURLException { + URL url = jarFile.toURI().toURL(); addURL(url); } @@ -98,6 +91,6 @@ public static MainClassLoader findAncestor(ClassLoader cl) { */ @SuppressWarnings("unused") private void appendToClassPathForInstrumentation(String jarfile) throws IOException { - add(Paths.get(jarfile).toRealPath().toUri().toURL()); + addJarToClasspath(Paths.get(jarfile).toRealPath().toFile()); } } diff --git a/src/org/omegat/filters2/master/PluginUtils.java b/src/org/omegat/filters2/master/PluginUtils.java index dccdd3ec44..6fd0e64f83 100644 --- a/src/org/omegat/filters2/master/PluginUtils.java +++ b/src/org/omegat/filters2/master/PluginUtils.java @@ -194,12 +194,10 @@ public static void loadPlugins(final Map params) { try (InputStream in = mu.openStream()) { Manifest m = new Manifest(in); if ("org.omegat.Main".equals(m.getMainAttributes().getValue("Main-Class"))) { - // found main manifest - not in development mode + // found a main manifest - not in development mode foundMain = true; - loadFromManifest(m, pluginsClassLoader, null); - } else { - loadFromManifest(m, pluginsClassLoader, mu); } + loadFromManifest(m, pluginsClassLoader, mu); if ("theme".equals(m.getMainAttributes().getValue("Plugin-Category"))) { String target = mu.toString(); for (URL url : urlList) { @@ -211,9 +209,7 @@ public static void loadPlugins(final Map params) { } catch (ClassNotFoundException e) { Log.log(e); } catch (UnsupportedClassVersionError e) { - JarURLConnection connection = (JarURLConnection) mu.openConnection(); - URL url = connection.getJarFileURL(); - Log.logWarningRB("PLUGIN_JAVA_VERSION_ERROR", url); + Log.logWarningRB("PLUGIN_JAVA_VERSION_ERROR", getJarFileUrlFromResourceUrl(mu)); } } } catch (IOException ex) { @@ -633,4 +629,9 @@ private static boolean loadClassOld(String sType, String key, ClassLoader classL public static Collection getPluginInformations() { return Collections.unmodifiableSet(PLUGIN_INFORMATIONS); } + + public static URL getJarFileUrlFromResourceUrl(URL url) throws IOException { + JarURLConnection connection = (JarURLConnection) url.openConnection(); + return connection.getJarFileURL(); + } } diff --git a/src/org/omegat/util/PluginInstaller.java b/src/org/omegat/util/PluginInstaller.java index 033934d0a9..1e0d755849 100644 --- a/src/org/omegat/util/PluginInstaller.java +++ b/src/org/omegat/util/PluginInstaller.java @@ -3,7 +3,7 @@ with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. - Copyright (C) 2021-2022 Hiroshi Miura + Copyright (C) 2021-2023 Hiroshi Miura Home page: https://www.omegat.org/ Support center: https://omegat.org/support @@ -25,6 +25,8 @@ package org.omegat.util; +import java.awt.BorderLayout; +import java.awt.Dimension; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -33,26 +35,33 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.function.Predicate; import java.util.jar.Attributes; import java.util.jar.Manifest; +import java.util.stream.Collectors; import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.table.AbstractTableModel; import org.apache.commons.io.FileUtils; import org.omegat.core.Core; import org.omegat.core.data.PluginInformation; import org.omegat.filters2.master.PluginUtils; -import org.omegat.gui.preferences.view.PluginsPreferencesController; - /** * Plugin installer utility class. @@ -61,8 +70,6 @@ */ public final class PluginInstaller { - private static final String PLUGIN_NAME = "Plugin-Name"; - private static final String PLUGIN_VERSION = "Plugin-Version"; private static final String PLUGIN_TYPE = "OmegaT-Plugin"; private PluginInstaller() { @@ -70,7 +77,7 @@ private PluginInstaller() { public static boolean install(final File pluginFile) { Path pluginJarFile; - PluginInformation info; + Set infoSet; try { // unpack or copy jar to temporary directory Path tmporaryDir = Files.createTempDirectory("omegat"); @@ -83,10 +90,14 @@ public static boolean install(final File pluginFile) { return false; } - // check manifest + // check a manifest and retrieve a set of plugin information try { - info = parsePluginJarFileManifest(pluginJarFile.toFile()).stream().findFirst() - .orElseThrow(() -> new IOException(OStrings.getString("PREFS_PLUGINS_UNKNOWN_ARCHIVE"))); + infoSet = parsePluginJarFileManifest(pluginJarFile.toFile()); + if (infoSet.isEmpty()) { + Log.logWarningRB("PREFS_PLUGINS_UNKNOWN_ARCHIVE"); + Core.getMainWindow().displayWarningRB("PREFS_PLUGINS_UNKNOWN_ARCHIVE"); + return false; + } } catch (IOException e) { // it is not a plugin jar file. Log.logErrorRB("PREFS_PLUGINS_INSTALLATION_FAILED", e.getLocalizedMessage()); @@ -94,32 +105,66 @@ public static boolean install(final File pluginFile) { e.getLocalizedMessage()); return false; } - // Get plugin name and version to be installed. + Map installed = getInstalledPlugins(); + Set currentSet = infoSet.stream().map(PluginInformation::getClassName) + .map(installed::get).filter(Objects::nonNull).collect(Collectors.toSet()); + PluginInformation info = infoSet.iterator().next(); + // Get a plugin name and version to be installed. String pluginName = info.getName(); String version = info.getVersion(); // detect current installation - PluginInformation currentInfo = getInstalledPlugins().getOrDefault(info.getClassName(), null); + PluginInformation currentInfo = installed.getOrDefault(info.getClassName(), null); + String title = ""; String message; if (currentInfo != null) { - message = StringUtil.format(OStrings.getString("PREFS_PLUGINS_CONFIRM_UPGRADE"), pluginName, - currentInfo.getVersion(), version); + if (currentInfo.getVersion().equals(version)) { + title = OStrings.getString("PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION"); + message = StringUtil.format(OStrings.getString("PREFS_PLUGINS_CONFIRM_OVERWRITE"), pluginName, + currentInfo.getVersion(), version); + } else { + title = OStrings.getString("PREFS_PLUGINS_TITLE_CONFIRM_UPGRADE"); + message = StringUtil.format(OStrings.getString("PREFS_PLUGINS_CONFIRM_UPGRADE"), pluginName, + currentInfo.getVersion(), version); + } } else { + title = OStrings.getString("PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION"); message = StringUtil.format(OStrings.getString("PREFS_PLUGINS_CONFIRM_INSTALL"), pluginName, version); } + JPanel confirmPanel = new JPanel(); + confirmPanel.setLayout(new BorderLayout()); + JTextArea msg = new JTextArea(message); + msg.setEditable(false); + if (Math.max(installed.size(), currentSet.size()) > 1) { + String[] titles = {OStrings.getString("PREFS_PLUGINS_TITLE_NAME"), + OStrings.getString("PREFS_PLUGINS_TITLE_CURRENT_VERSION"), + OStrings.getString("PREFS_PLUGINS_TITLE_TARGET_VERSION") + }; + JTable compareTable = new JTable(); + compareTable.setModel(new PluginInstallerTableModel(titles, currentSet, + infoSet)); + confirmPanel.setPreferredSize(new Dimension(400, 200)); + confirmPanel.add(compareTable.getTableHeader(), BorderLayout.NORTH); + confirmPanel.add(new JScrollPane(compareTable), BorderLayout.CENTER); + confirmPanel.add(msg, BorderLayout.SOUTH); + } else { + confirmPanel.add(msg, BorderLayout.CENTER); + } + // confirm installation - if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Core.getMainWindow().getApplicationFrame(), - message, - OStrings.getString("PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION"), - JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE)) { + if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog( + Core.getMainWindow().getApplicationFrame(), confirmPanel, title, JOptionPane.OK_CANCEL_OPTION, + JOptionPane.INFORMATION_MESSAGE)) { if (doInstall(currentInfo, pluginJarFile.toFile())) { + JOptionPane.showMessageDialog(Core.getMainWindow().getApplicationFrame(), + OStrings.getString("PREFS_PLUGINS_INSTALLATION_SUCCEED")); return true; } JOptionPane.showConfirmDialog(Core.getMainWindow().getApplicationFrame(), OStrings.getString("PREFS_PLUGINS_INSTALLATION_FAILED"), - OStrings.getString("PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION"), - JOptionPane.YES_OPTION, JOptionPane.ERROR_MESSAGE); + OStrings.getString("PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION"), JOptionPane.YES_OPTION, + JOptionPane.ERROR_MESSAGE); } return false; } @@ -128,14 +173,19 @@ private static boolean doInstall(PluginInformation currentInfo, File file) { try { if (currentInfo != null) { URL url = currentInfo.getUrl(); - File jarFile = new File(url.getPath().substring(5, url.getPath().indexOf("!"))); - if (jarFile.getName().equals(file.getName())) { - // try to override? - File bakFile = new File(jarFile.getPath() + ".bak"); - FileUtils.moveFile(jarFile, bakFile); - FileUtils.forceDeleteOnExit(bakFile); - } else { - FileUtils.forceDeleteOnExit(jarFile); + if (url != null) { + File jarFile = new File(url.getPath().substring(5, url.getPath().indexOf("!"))); + if (!jarFileInInstallDir(jarFile)) { + // plugin file may be in ~/.omegat/plugins/ + if (jarFile.getName().equals(file.getName())) { + // try to override? + File bakFile = new File(jarFile.getPath() + ".bak"); + FileUtils.moveFile(jarFile, bakFile); + FileUtils.forceDeleteOnExit(bakFile); + } else { + FileUtils.forceDeleteOnExit(jarFile); + } + } } } File homePluginsDir = new File(StaticUtils.getConfigDir(), "plugins"); @@ -149,12 +199,29 @@ private static boolean doInstall(PluginInformation currentInfo, File file) { } /** - * Unpack plugin file when necessary and copy it. + * Check if jarFile is placed in OmegaT installed system directory. + * + * @param jarFile + * a file determine. + * @return true when a file is under installed directory, otherwise return + * false. + */ + private static boolean jarFileInInstallDir(File jarFile) { + Path installDir = Paths.get(StaticUtils.installDir()).normalize(); + Path jarPath = jarFile.toPath().normalize(); + return jarPath.startsWith(installDir); + } + + /** + * Unpack a plugin file when necessary and copy it. * - * @param sourceFile plugin soure file to be installed (jar or zip) - * @param targetPath target path to be installed. - * @return installed plugin jar file path. - * @throws IOException when source file is corrupted. + * @param sourceFile + * plugin source file to be installed (jar or zip) + * @param targetPath + * target path to be installed. + * @return jar file path of installed plugin. + * @throws IOException + * when source file is corrupted. */ static Path unpackPlugin(File sourceFile, Path targetPath) throws IOException { Path target; @@ -164,8 +231,9 @@ static Path unpackPlugin(File sourceFile, Path targetPath) throws IOException { } else if (sourceFile.getName().endsWith(".zip")) { try (InputStream inputStream = Files.newInputStream(sourceFile.toPath())) { Predicate expected = f -> f.endsWith(OConsts.JAR_EXTENSION); - List extracted = StaticUtils.extractFromZip(inputStream, targetPath.toFile(), expected); - if (extracted.size() == 0) { + List extracted = StaticUtils.extractFromZip(inputStream, targetPath.toFile(), + expected); + if (extracted.isEmpty()) { throw new FileNotFoundException("Could not extract a jar file from zip"); } target = targetPath.resolve(extracted.get(0)); @@ -180,19 +248,24 @@ static Path unpackPlugin(File sourceFile, Path targetPath) throws IOException { /** * Parse Manifest from plugin jar file. - * @param pluginJarFile plugin jar file + * + * @param pluginJarFile + * plugin jar file * @return PluginInformation */ static Set parsePluginJarFileManifest(File pluginJarFile) throws IOException { Set pluginInfo = new HashSet<>(); URL[] urls = new URL[1]; urls[0] = pluginJarFile.toURI().toURL(); - try (URLClassLoader pluginsClassLoader = new URLClassLoader(urls, - PluginsPreferencesController.class.getClassLoader())) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + try (URLClassLoader pluginsClassLoader = new URLClassLoader(urls, cl)) { for (Enumeration mlist = pluginsClassLoader.getResources("META-INF/MANIFEST.MF"); mlist .hasMoreElements();) { URL mu = mlist.nextElement(); - pluginInfo.addAll(parsePluginJarFileManifest(mu)); + if (Files.isSameFile(pluginJarFile.toPath(), + Paths.get(PluginUtils.getJarFileUrlFromResourceUrl(mu).getFile()))) { + pluginInfo.addAll(parsePluginJarFileManifest(mu)); + } } } return pluginInfo; @@ -200,14 +273,14 @@ static Set parsePluginJarFileManifest(File pluginJarFile) thr /** * - * @param manifestUrl URL of MANIFEST.MF file + * @param manifestUrl + * URL of MANIFEST.MF file * @return plugin information */ static Set parsePluginJarFileManifest(URL manifestUrl) throws IOException { Set pluginInfo = new HashSet<>(); try (InputStream in = manifestUrl.openStream()) { Manifest m = new Manifest(in); - Attributes mainAttrs = m.getMainAttributes(); String pluginClasses = m.getMainAttributes().getValue("OmegaT-Plugins"); if (pluginClasses != null) { for (String clazz : pluginClasses.split("\\s+")) { @@ -238,7 +311,8 @@ static Set parsePluginJarFileManifest(URL manifestUrl) throws /** * Return installed plugins. - * @return Set of PluginInformation + * + * @return Map of PluginInformation */ private static Map getInstalledPlugins() { Map installedPlugins = new TreeMap<>(); @@ -248,4 +322,66 @@ private static Map getInstalledPlugins() { .forEach(info -> installedPlugins.put(info.getClassName(), info)); return installedPlugins; } + + @SuppressWarnings("serial") + static class PluginInstallerTableModel extends AbstractTableModel { + + private final List current = new ArrayList<>(); + private final List installer = new ArrayList<>(); + private final List classes = new ArrayList<>(); + private final String[] titles; + + PluginInstallerTableModel(String[] titles, Set current, + Set installer) { + this.titles = titles; + this.current.addAll(current); + this.installer.addAll(installer); + classes.addAll( + current.stream().map(PluginInformation::getClassName).collect(Collectors.toList())); + classes.addAll(installer.stream().map(PluginInformation::getClassName) + .filter(className -> current.stream().noneMatch(j -> j.getClassName().equals(className))) + .collect(Collectors.toList())); + } + + @Override + public String getColumnName(int index) { + return titles[index]; + } + + @Override + public int getRowCount() { + return classes.size(); + } + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (rowIndex < 0) { + return null; + } + String target = classes.get(rowIndex); + if (columnIndex == 0) { + return installer.stream().filter(i -> i.getClassName().equals(target)).findFirst() + .map(PluginInformation::getName).orElse(""); + } + if (columnIndex == 1) { + var currentPlugin = current.stream().filter(i -> i.getClassName().equals(target)).findFirst(); + if (currentPlugin.isPresent()) { + return currentPlugin.map(PluginInformation::getVersion).filter(v -> !v.isEmpty()) + .orElse("N.A."); + } else { + return "-"; + } + } + if (columnIndex == 2) { + return installer.stream().filter(i -> i.getClassName().equals(target)).findFirst() + .map(PluginInformation::getVersion).filter(v -> !v.isEmpty()).orElse("N.A."); + } + return null; + } + } } From 2308d9ea2c02d7756b32fc402665359285b64bc3 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Thu, 21 Mar 2024 01:21:20 +0900 Subject: [PATCH 10/12] test: change case for ExternalTMFactoryTest (#819) - the case tm by xliff1 - Update case with "state=final and translate=no" Signed-off-by: Hiroshi Miura --- test/data/filters/xliff/filters4-xliff1/en-ca.xlf | 7 +++++-- test/src/org/omegat/core/data/ExternalTMFactoryTest.java | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/data/filters/xliff/filters4-xliff1/en-ca.xlf b/test/data/filters/xliff/filters4-xliff1/en-ca.xlf index 56e0971612..c3fe8fcc0c 100644 --- a/test/data/filters/xliff/filters4-xliff1/en-ca.xlf +++ b/test/data/filters/xliff/filters4-xliff1/en-ca.xlf @@ -6,7 +6,7 @@ This is a test Això és una prova - + This is another test Això és una altra prova @@ -14,6 +14,9 @@ This is a house Això és una casa - + +This is not a target +Això és una casa + diff --git a/test/src/org/omegat/core/data/ExternalTMFactoryTest.java b/test/src/org/omegat/core/data/ExternalTMFactoryTest.java index 4ce704c466..8ead8a2215 100644 --- a/test/src/org/omegat/core/data/ExternalTMFactoryTest.java +++ b/test/src/org/omegat/core/data/ExternalTMFactoryTest.java @@ -148,6 +148,10 @@ public void testLoadXliff() throws Exception { assertEquals(3, tmx.getEntries().size()); assertEquals("This is a test", tmx.getEntries().get(0).getSourceText()); assertEquals("Això és una prova", tmx.getEntries().get(0).getTranslationText()); + assertEquals("This is another test", tmx.getEntries().get(1).getSourceText()); + assertEquals("Això és una altra prova", tmx.getEntries().get(1).getTranslationText()); + assertEquals("This is a house", tmx.getEntries().get(2).getSourceText()); + assertEquals("Això és una casa", tmx.getEntries().get(2).getTranslationText()); } /** From 2778c62e9871d2cbdef8db4c77cd102c1ed87a82 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Thu, 21 Mar 2024 01:25:03 +0900 Subject: [PATCH 11/12] refactor: ProjectPropertiesDialog class (#967) * refactor: ProjectPropertiesDialog class - Split controller logic into ProjectPropertiesDialogController class - Reduce arguments of doOK method - remove suppression of checkstyle - Introduce static ProjectPropertiesDialogController.showDialog function Signed-off-by: Hiroshi Miura * style: fix unused imports Signed-off-by: Hiroshi Miura * refactor: ProjectPropertiesDialogController#doBrowseDirectory Signed-off-by: Hiroshi Miura * style: better handling of dialogType Signed-off-by: Hiroshi Miura * refactor: rename internal method name Signed-off-by: Hiroshi Miura * refactor: revert ProjectPropertiesDialog.MODE Signed-off-by: Hiroshi Miura * refactor: give component name for test Signed-off-by: Hiroshi Miura * style: wip Signed-off-by: Hiroshi Miura --------- Signed-off-by: Hiroshi Miura --- config/checkstyle/suppressions.xml | 1 - .../gui/dialogs/ProjectPropertiesDialog.java | 1019 +++++------------ .../ProjectPropertiesDialogController.java | 603 ++++++++++ .../omegat/gui/main/ProjectUICommands.java | 18 +- .../org/omegat/gui/dialogs/DialogsTest.java | 2 +- 5 files changed, 866 insertions(+), 777 deletions(-) create mode 100644 src/org/omegat/gui/dialogs/ProjectPropertiesDialogController.java diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index 4caefc309d..e8874b3b41 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -107,7 +107,6 @@ - diff --git a/src/org/omegat/gui/dialogs/ProjectPropertiesDialog.java b/src/org/omegat/gui/dialogs/ProjectPropertiesDialog.java index 61c2d63249..859860d62a 100644 --- a/src/org/omegat/gui/dialogs/ProjectPropertiesDialog.java +++ b/src/org/omegat/gui/dialogs/ProjectPropertiesDialog.java @@ -9,6 +9,7 @@ 2012 Didier Briel, Aaron Madlon-Kay 2013 Aaron Madlon-Kay, Yu Tang 2014-2015 Aaron Madlon-Kay + 2024 Hiroshi Miura Home page: https://www.omegat.org/ Support center: https://omegat.org/support @@ -40,14 +41,10 @@ import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.File; -import java.util.ArrayList; import java.util.Comparator; -import java.util.List; import java.util.Vector; -import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -56,7 +53,6 @@ import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; @@ -72,28 +68,15 @@ import org.omegat.core.Core; import org.omegat.core.data.CommandVarExpansion; import org.omegat.core.data.ProjectProperties; -import org.omegat.core.segmentation.SRX; -import org.omegat.externalfinder.ExternalFinder; -import org.omegat.externalfinder.gui.ExternalFinderCustomizer; -import org.omegat.externalfinder.item.ExternalFinderConfiguration; -import org.omegat.filters2.master.FilterMaster; import org.omegat.filters2.master.PluginUtils; -import org.omegat.gui.filters2.FiltersCustomizer; -import org.omegat.gui.segmentation.SegmentationCustomizer; import org.omegat.util.Language; import org.omegat.util.Log; -import org.omegat.util.OConsts; import org.omegat.util.OStrings; import org.omegat.util.Preferences; -import org.omegat.util.StringUtil; import org.omegat.util.gui.LanguageComboBoxRenderer; -import org.omegat.util.gui.OmegaTFileChooser; import org.omegat.util.gui.StaticUIUtils; import org.omegat.util.gui.TokenizerComboBoxRenderer; -import gen.core.filters.Filters; -import gen.core.project.RepositoryDefinition; - /** * The dialog for customizing the OmegaT project (where project properties are * entered and/or modified). @@ -116,14 +99,13 @@ */ @SuppressWarnings("serial") public class ProjectPropertiesDialog extends JDialog { - private ProjectProperties projectProperties; public enum Mode { /** This dialog is used to create a new project. */ NEW_PROJECT, /** - * This dialog is used to resolve missing directories of existing project - * (upon opening the project). + * This dialog is used to resolve missing directories of existing + * project (upon opening the project). */ RESOLVE_DIRS, /** @@ -136,24 +118,17 @@ public enum Mode { /** * The type of the dialog: *
    - *
  • Creating project == {@link Mode#NEW_PROJECT} + *
  • Creating project == + * {@link ProjectPropertiesDialog.Mode#NEW_PROJECT} *
  • Resolving the project's directories (existing project with some dirs - * missing) == {@link Mode#RESOLVE_DIRS} - *
  • Editing project properties == {@link Mode#EDIT_PROJECT} + * missing) == {@link ProjectPropertiesDialog.Mode#RESOLVE_DIRS} + *
  • Editing project properties == + * {@link ProjectPropertiesDialog.Mode#EDIT_PROJECT} *
*/ - private Mode dialogType; - - /** Project SRX. */ - private SRX srx; - - /** Project filters. */ - private Filters filters; + private final Mode dialogType; - /** Project ExternalFinder config */ - private ExternalFinderConfiguration externalFinderConfig; - - private List srcExcludes = new ArrayList<>(); + private final ProjectPropertiesDialogController controller; /** * Creates a dialog to create a new project / edit folders of existing one. @@ -163,26 +138,83 @@ public enum Mode { * @param projFileName * project file name * @param dialogTypeValue - * type of the dialog ({@link Mode#NEW_PROJECT}, {@link Mode#RESOLVE_DIRS} or {@link Mode#EDIT_PROJECT}). + * type of the dialog + * ({@link ProjectPropertiesDialog.Mode#NEW_PROJECT}, + * {@link ProjectPropertiesDialog.Mode#RESOLVE_DIRS} or + * {@link ProjectPropertiesDialog.Mode#EDIT_PROJECT}). */ - // CHECKSTYLE:OFF public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProperties, String projFileName, - Mode dialogTypeValue) { + Mode dialogTypeValue) { super(parent, true); - this.projectProperties = projectProperties; - this.srx = projectProperties.getProjectSRX(); this.dialogType = dialogTypeValue; - this.filters = projectProperties.getProjectFilters(); - srcExcludes.addAll(projectProperties.getSourceRootExcludes()); - externalFinderConfig = ExternalFinder.getProjectConfig(); + initializeComponents(); + controller = new ProjectPropertiesDialogController(parent, this, dialogTypeValue, projectProperties); + if (dialogType == Mode.RESOLVE_DIRS) { + setResolveDirsDefaults(); + } + setName(DIALOG_NAME); + setLocationRelativeTo(parent); - Border emptyBorder = new EmptyBorder(2, 0, 2, 0); + // Pack once to get the width... + pack(); + updateUIText(); + // Then again to expand the height to accomodate the message. + // This is needed because the height isn't known until the + // amount of linewrapping is known. + pack(); + // The result is still slightly too small on some LAFs, so enlarge + // slightly with magic numbers. + setSize(9 * getWidth() / 8, getHeight() + 10); + setResizable(true); + + StaticUIUtils.fitInScreen(this); + } + + private void initializeComponents() { Box centerBox = new ScrollableBox(BoxLayout.Y_AXIS); - // Have to set background and opacity on OS X or else entire dialog is white. + // Have to set background and opacity on OS X or else the entire dialog + // is white. centerBox.setBackground(getBackground()); centerBox.setOpaque(true); centerBox.setBorder(new EmptyBorder(5, 5, 5, 5)); + Box localesBox = createLocalesBox(); + centerBox.add(localesBox); + + // options + centerBox.add(Box.createVerticalStrut(5)); + JPanel optionsBox = createOptionsBox(); + centerBox.add(optionsBox, BorderLayout.WEST); + + // directories + centerBox.add(Box.createVerticalStrut(5)); + Box dirsBox = createDirsBox(); + centerBox.add(dirsBox); + + JScrollPane scrollPane = new JScrollPane(centerBox); + // Prevent an ugly white viewport background with GTK LAF + scrollPane.setBackground(getBackground()); + scrollPane.getViewport().setOpaque(false); + getContentPane().add(scrollPane, "Center"); + Mnemonics.setLocalizedText(okButton, OStrings.getString("BUTTON_OK")); + setName(OK_BUTTON_NAME); + getRootPane().setDefaultButton(okButton); + Mnemonics.setLocalizedText(cancelButton, OStrings.getString("BUTTON_CANCEL")); + setName(CANCEL_BUTTON_NAME); + + Box southBox = Box.createHorizontalBox(); + southBox.setBorder(new EmptyBorder(5, 5, 5, 5)); + southBox.add(Box.createHorizontalGlue()); + southBox.add(okButton); + southBox.add(Box.createHorizontalStrut(5)); + southBox.add(cancelButton); + getContentPane().add(southBox, "South"); + + setResizable(false); + } + + private Box createLocalesBox() { + Border emptyBorder = new EmptyBorder(2, 0, 2, 0); // Source and target languages and tokenizers Box localesBox = Box.createHorizontalBox(); localesBox.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), @@ -202,13 +234,13 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp bL.add(bSL); // Source language field - final JComboBox sourceLocaleField = new JComboBox<>(new Vector<>(Language.getLanguages())); + sourceLocaleField = new JComboBox<>(new Vector<>(Language.getLanguages())); + sourceLocaleField.setName(SOURCE_LOCALE_CB_NAME); if (sourceLocaleField.getMaximumRowCount() < 20) { sourceLocaleField.setMaximumRowCount(20); } sourceLocaleField.setEditable(true); sourceLocaleField.setRenderer(new LanguageComboBoxRenderer()); - sourceLocaleField.setSelectedItem(projectProperties.getSourceLanguage()); bL.add(sourceLocaleField); // Target language label @@ -221,20 +253,20 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp bL.add(bLL); // Target language field - final JComboBox targetLocaleField = new JComboBox<>(new Vector<>(Language.getLanguages())); + targetLocaleField = new JComboBox<>(new Vector<>(Language.getLanguages())); + targetLocaleField.setName(TARGET_LOCALE_CB_NAME); if (targetLocaleField.getMaximumRowCount() < 20) { targetLocaleField.setMaximumRowCount(20); } targetLocaleField.setEditable(true); targetLocaleField.setRenderer(new LanguageComboBoxRenderer()); - targetLocaleField.setSelectedItem(projectProperties.getTargetLanguage()); bL.add(targetLocaleField); // Tokenizers box Box bT = Box.createVerticalBox(); localesBox.add(bT); - Class[] tokenizers = PluginUtils.getTokenizerClasses().stream().sorted(Comparator.comparing(Class::getName)) - .toArray(Class[]::new); + Class[] tokenizers = PluginUtils.getTokenizerClasses().stream() + .sorted(Comparator.comparing(Class::getName)).toArray(Class[]::new); // Source tokenizer label JLabel sourceTokenizerLabel = new JLabel(); @@ -246,13 +278,13 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp bT.add(bST); // Source tokenizer field - final JComboBox> sourceTokenizerField = new JComboBox<>(tokenizers); + sourceTokenizerField = new JComboBox<>(tokenizers); + sourceTokenizerField.setName(SOURCE_TOKENIZER_FIELD_NAME); if (sourceTokenizerField.getMaximumRowCount() < 20) { sourceTokenizerField.setMaximumRowCount(20); } sourceTokenizerField.setEditable(false); sourceTokenizerField.setRenderer(new TokenizerComboBoxRenderer()); - sourceTokenizerField.setSelectedItem(projectProperties.getSourceTokenizer()); bT.add(sourceTokenizerField); String cliTokSrc = Core.getParams().get(CLIParameters.TOKENIZER_SOURCE); @@ -267,19 +299,6 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp } } - ActionListener sourceLocaleListener = e -> { - if (!sourceLocaleField.isEnabled()) { - return; - } - Object newLang = sourceLocaleField.getSelectedItem(); - if (newLang instanceof String) { - newLang = new Language((String) newLang); - } - Class newTok = PluginUtils.getTokenizerClassForLanguage((Language) newLang); - sourceTokenizerField.setSelectedItem(newTok); - }; - sourceLocaleField.addActionListener(sourceLocaleListener); - // Target tokenizer label JLabel targetTokenizerLabel = new JLabel(); Mnemonics.setLocalizedText(targetTokenizerLabel, OStrings.getString("PP_LOC_TOK")); @@ -290,13 +309,13 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp bT.add(bTT); // Target tokenizer field - final JComboBox> targetTokenizerField = new JComboBox<>(tokenizers); + targetTokenizerField = new JComboBox<>(tokenizers); + targetTokenizerField.setName(TARGET_TOKENIZER_FIELD_NAME); if (targetTokenizerField.getMaximumRowCount() < 20) { targetTokenizerField.setMaximumRowCount(20); } targetTokenizerField.setEditable(false); targetTokenizerField.setRenderer(new TokenizerComboBoxRenderer()); - targetTokenizerField.setSelectedItem(projectProperties.getTargetTokenizer()); bT.add(targetTokenizerField); String cliTokTrg = Core.getParams().get(CLIParameters.TOKENIZER_TARGET); @@ -311,30 +330,10 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp } } + return localesBox; + } - ActionListener targetLocaleListener = e -> { - if (!targetLocaleField.isEnabled()) { - return; - } - Object newLang = targetLocaleField.getSelectedItem(); - if (newLang instanceof String) { - newLang = new Language((String) newLang); - } - Class newTok = PluginUtils.getTokenizerClassForLanguage((Language) newLang); - targetTokenizerField.setSelectedItem(newTok); - }; - targetLocaleField.addActionListener(targetLocaleListener); - - centerBox.add(localesBox); - - if (dialogTypeValue == Mode.NEW_PROJECT) { - // Infer appropriate tokenizers from source languages - sourceLocaleListener.actionPerformed(null); - targetLocaleListener.actionPerformed(null); - } - - // options - centerBox.add(Box.createVerticalStrut(5)); + private JPanel createOptionsBox() { JPanel optionsBox = new JPanel(new GridBagLayout()); optionsBox.setBorder(new EtchedBorder()); optionsBox.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), @@ -343,56 +342,55 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp gbc.insets = new Insets(3, 3, 3, 3); // sentence-segmenting - final JCheckBox sentenceSegmentingCheckBox = new JCheckBox(); - Mnemonics - .setLocalizedText(sentenceSegmentingCheckBox, OStrings.getString("PP_SENTENCE_SEGMENTING")); + Mnemonics.setLocalizedText(sentenceSegmentingCheckBox, OStrings.getString("PP_SENTENCE_SEGMENTING")); + sentenceSegmentingCheckBox.setName(SENTENCE_SEGMENTING_CB_NAME); gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; optionsBox.add(sentenceSegmentingCheckBox, gbc); - JButton sentenceSegmentingButton = new JButton(); Mnemonics.setLocalizedText(sentenceSegmentingButton, OStrings.getString("MW_OPTIONSMENU_LOCAL_SENTSEG")); + sentenceSegmentingButton.setName(SENTENCE_SEGMENTING_BUTTON_NAME); gbc.gridx = 1; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST; optionsBox.add(sentenceSegmentingButton, gbc); // File Filters - JButton fileFiltersButton = new JButton(); Mnemonics.setLocalizedText(fileFiltersButton, OStrings.getString("TF_MENU_DISPLAY_LOCAL_FILTERS")); + fileFiltersButton.setName(FILE_FILTER_BUTTON_NAME); gbc.gridx = 1; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; optionsBox.add(fileFiltersButton, gbc); // Repositories mapping - JButton repositoriesButton = new JButton(); Mnemonics.setLocalizedText(repositoriesButton, OStrings.getString("PP_REPOSITORIES")); + repositoriesButton.setName(REPOSITORIES_BUTTON_NAME); gbc.gridx = 1; gbc.gridy = 2; gbc.anchor = GridBagConstraints.EAST; optionsBox.add(repositoriesButton, gbc); // Repositories mapping - JButton externalFinderButton = new JButton(); Mnemonics.setLocalizedText(externalFinderButton, OStrings.getString("PP_EXTERNALFINDER")); + externalFinderButton.setName(EXTERNAL_FINDER_BUTTON_NAME); gbc.gridx = 1; gbc.gridy = 3; gbc.anchor = GridBagConstraints.EAST; optionsBox.add(externalFinderButton, gbc); // multiple translations - final JCheckBox allowDefaultsCheckBox = new JCheckBox(); Mnemonics.setLocalizedText(allowDefaultsCheckBox, OStrings.getString("PP_ALLOW_DEFAULTS")); + allowDefaultsCheckBox.setName(ALLOW_DEFAULTS_CB_NAME); gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; optionsBox.add(allowDefaultsCheckBox, gbc); // Remove Tags - final JCheckBox removeTagsCheckBox = new JCheckBox(); Mnemonics.setLocalizedText(removeTagsCheckBox, OStrings.getString("PP_REMOVE_TAGS")); + removeTagsCheckBox.setName(REMOVE_TAGS_CB_NAME); gbc.gridx = 0; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; @@ -404,10 +402,8 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp gbc.gridy = 3; gbc.anchor = GridBagConstraints.WEST; optionsBox.add(externalCommandLabel, gbc); - final JTextArea externalCommandTextArea = new JTextArea(); externalCommandTextArea.setRows(2); externalCommandTextArea.setLineWrap(true); - externalCommandTextArea.setText(projectProperties.getExternalCommand()); if (Preferences.isPreference(Preferences.ALLOW_PROJECT_EXTERN_CMD)) { Mnemonics.setLocalizedText(externalCommandLabel, OStrings.getString("PP_EXTERNAL_COMMAND")); } else { @@ -417,6 +413,7 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp externalCommandLabel.setToolTipText(OStrings.getString("PP_EXTERN_CMD_DISABLED_TOOLTIP")); externalCommandTextArea.setBackground(getBackground()); } + externalCommandTextArea.setName(EXTERNAL_COMMAND_TEXTAREA_NAME); final JScrollPane externalCommandScrollPane = new JScrollPane(); externalCommandScrollPane.setViewportView(externalCommandTextArea); gbc.gridx = 0; @@ -426,12 +423,12 @@ public ProjectPropertiesDialog(Frame parent, final ProjectProperties projectProp gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; optionsBox.add(externalCommandScrollPane, gbc); - final JLabel variablesLabel = new javax.swing.JLabel(); - final JComboBox variablesList = new JComboBox<>( - new Vector<>(CommandVarExpansion.getCommandVariables())); - final JButton insertButton = new javax.swing.JButton(); - // Add variable insertion controls only if project external commands are enabled. + variablesList = new JComboBox<>(new Vector<>(CommandVarExpansion.getCommandVariables())); + variablesList.setName(VARIABLE_LIST_NAME); + // Add variable insertion controls only if project external commands are + // enabled. if (Preferences.isPreference(Preferences.ALLOW_PROJECT_EXTERN_CMD)) { + Border emptyBorder = new EmptyBorder(2, 0, 2, 0); Box bIC = Box.createHorizontalBox(); bIC.setBorder(emptyBorder); Mnemonics.setLocalizedText(variablesLabel, @@ -455,12 +452,11 @@ public void actionPerformed(ActionEvent e) { gbc.fill = GridBagConstraints.HORIZONTAL; optionsBox.add(bIC, gbc); } + return optionsBox; + } - centerBox.add(optionsBox, BorderLayout.WEST); - - // directories - centerBox.add(Box.createVerticalStrut(5)); - + private Box createDirsBox() { + Border emptyBorder = new EmptyBorder(2, 0, 2, 0); Box dirsBox = Box.createVerticalBox(); dirsBox.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), OStrings.getString("PP_DIRECTORIES"))); @@ -471,13 +467,11 @@ public void actionPerformed(ActionEvent e) { bSrc.setBorder(emptyBorder); bSrc.add(srcRootLabel); bSrc.add(Box.createHorizontalGlue()); - JButton srcExcludesBtn = new JButton(); Mnemonics.setLocalizedText(srcExcludesBtn, OStrings.getString("PP_BUTTON_BROWSE_SRC_EXCLUDES")); + srcExcludesBtn.setName(SRC_EXCLUDES_BUTTON_NAME); bSrc.add(srcExcludesBtn); - JButton srcBrowse = new JButton(); Mnemonics.setLocalizedText(srcBrowse, OStrings.getString("PP_BUTTON_BROWSE_SRC")); bSrc.add(srcBrowse); - final JTextField srcRootField = new JTextField(); dirsBox.add(bSrc); dirsBox.add(srcRootField); @@ -487,10 +481,9 @@ public void actionPerformed(ActionEvent e) { bTM.setBorder(emptyBorder); bTM.add(tmRootLabel); bTM.add(Box.createHorizontalGlue()); - JButton tmBrowse = new JButton(); Mnemonics.setLocalizedText(tmBrowse, OStrings.getString("PP_BUTTON_BROWSE_TM")); + tmBrowse.setName(TM_BROWSE_BUTTON_NAME); bTM.add(tmBrowse); - final JTextField tmRootField = new JTextField(); dirsBox.add(bTM); dirsBox.add(tmRootField); @@ -500,10 +493,9 @@ public void actionPerformed(ActionEvent e) { bGlos.setBorder(emptyBorder); bGlos.add(glosRootLabel); bGlos.add(Box.createHorizontalGlue()); - JButton glosBrowse = new JButton(); Mnemonics.setLocalizedText(glosBrowse, OStrings.getString("PP_BUTTON_BROWSE_GL")); + glosBrowse.setName(GLOSSARY_BROWSE_BUTTON_NAME); bGlos.add(glosBrowse); - final JTextField glosRootField = new JTextField(); dirsBox.add(bGlos); dirsBox.add(glosRootField); @@ -513,10 +505,9 @@ public void actionPerformed(ActionEvent e) { bwGlos.setBorder(emptyBorder); bwGlos.add(writeableGlosLabel); bwGlos.add(Box.createHorizontalGlue()); - JButton wGlosBrowse = new JButton(); Mnemonics.setLocalizedText(wGlosBrowse, OStrings.getString("PP_BUTTON_BROWSE_WG")); + wGlosBrowse.setName(WRITABLE_GLOSSARY_BROWSE_BUTTON_NAME); bwGlos.add(wGlosBrowse); - final JTextField writeableGlosField = new JTextField(); dirsBox.add(bwGlos); dirsBox.add(writeableGlosField); @@ -526,10 +517,9 @@ public void actionPerformed(ActionEvent e) { bDict.setBorder(emptyBorder); bDict.add(locDictLabel); bDict.add(Box.createHorizontalGlue()); - JButton dictBrowse = new JButton(); Mnemonics.setLocalizedText(dictBrowse, OStrings.getString("PP_BUTTON_BROWSE_DICT")); + dictBrowse.setName(DICTIONARY_BROWSE_BUTTON_NAME); bDict.add(dictBrowse); - final JTextField dictRootField = new JTextField(); dirsBox.add(bDict); dirsBox.add(dictRootField); @@ -539,10 +529,9 @@ public void actionPerformed(ActionEvent e) { bLoc.setBorder(emptyBorder); bLoc.add(locRootLabel); bLoc.add(Box.createHorizontalGlue()); - JButton locBrowse = new JButton(); Mnemonics.setLocalizedText(locBrowse, OStrings.getString("PP_BUTTON_BROWSE_TAR")); + locBrowse.setName(LOC_BROWSE_BUTTON_NAME); bLoc.add(locBrowse); - final JTextField locRootField = new JTextField(); dirsBox.add(bLoc); dirsBox.add(locRootField); @@ -552,19 +541,22 @@ public void actionPerformed(ActionEvent e) { bExpTM.setBorder(emptyBorder); bExpTM.add(exportTMRootLabel); bExpTM.add(Box.createHorizontalGlue()); - JButton exportTMBrowse = new JButton(); Mnemonics.setLocalizedText(exportTMBrowse, OStrings.getString("PP_BUTTON_BROWSE_EXP_TM")); + exportTMBrowse.setName(EXPORT_TM_BROWSE_BUTTON_NAME); bExpTM.add(exportTMBrowse); - final JTextField exportTMRootField = new JTextField(); // Supply check boxes to choose which TM formats to export - final JCheckBox exportTMOmegaTCheckBox = new JCheckBox(OStrings.getString("PP_EXPORT_TM_OMEGAT")); - final JCheckBox exportTMLevel1CheckBox = new JCheckBox(OStrings.getString("PP_EXPORT_TM_LEVEL1")); - final JCheckBox exportTMLevel2CheckBox = new JCheckBox(OStrings.getString("PP_EXPORT_TM_LEVEL2")); + exportTMOmegaTCheckBox = new JCheckBox(OStrings.getString("PP_EXPORT_TM_OMEGAT")); + exportTMLevel1CheckBox = new JCheckBox(OStrings.getString("PP_EXPORT_TM_LEVEL1")); + exportTMLevel2CheckBox = new JCheckBox(OStrings.getString("PP_EXPORT_TM_LEVEL2")); + exportTMOmegaTCheckBox.setName(EXPORT_TM_OMEGAT_CB_NAME); + exportTMLevel1CheckBox.setName(EXPORT_TM_LEVEL1_CB_NAME); + exportTMLevel2CheckBox.setName(EXPORT_TM_LEVEL2_CB_NAME); JPanel exportTMPanel = new JPanel(new FlowLayout(FlowLayout.LEADING)); JLabel cbExportLabel = new JLabel(OStrings.getString("PP_EXPORT_TM_LEVELS")); dirsBox.add(bExpTM); + exportTMRootField.setName(EXPORT_TM_ROOT_FIELD_NAME); dirsBox.add(exportTMRootField); exportTMPanel.add(cbExportLabel); exportTMPanel.add(exportTMOmegaTCheckBox); @@ -572,636 +564,56 @@ public void actionPerformed(ActionEvent e) { exportTMPanel.add(exportTMLevel2CheckBox); dirsBox.add(exportTMPanel); - - - centerBox.add(dirsBox); - - JScrollPane scrollPane = new JScrollPane(centerBox); - // Prevent ugly white viewport background with GTK LAF - scrollPane.setBackground(getBackground()); - scrollPane.getViewport().setOpaque(false); - getContentPane().add(scrollPane, "Center"); - - JButton okButton = new JButton(); - Mnemonics.setLocalizedText(okButton, OStrings.getString("BUTTON_OK")); - getRootPane().setDefaultButton(okButton); - JButton cancelButton = new JButton(); - Mnemonics.setLocalizedText(cancelButton, OStrings.getString("BUTTON_CANCEL")); - - Box southBox = Box.createHorizontalBox(); - southBox.setBorder(new EmptyBorder(5, 5, 5, 5)); - southBox.add(Box.createHorizontalGlue()); - southBox.add(okButton); - southBox.add(Box.createHorizontalStrut(5)); - southBox.add(cancelButton); - getContentPane().add(southBox, "South"); - - setResizable(false); - - okButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doOK(sourceLocaleField, targetLocaleField, sourceTokenizerField, targetTokenizerField, - sentenceSegmentingCheckBox, srcRootField, locRootField, glosRootField, - writeableGlosField, tmRootField, exportTMRootField, exportTMOmegaTCheckBox, - exportTMLevel1CheckBox, exportTMLevel2CheckBox, dictRootField, allowDefaultsCheckBox, - removeTagsCheckBox, externalCommandTextArea); - } - }); - - cancelButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doCancel(); - } - }); - - srcBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doBrowseDirectoy(1, srcRootField); - } - }); - srcExcludesBtn.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - List result = FilenamePatternsEditorController.show(srcExcludes); - if (result != null) { - srcExcludes.clear(); - srcExcludes.addAll(result); - } - } - }); - - locBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doBrowseDirectoy(2, locRootField); - } - }); - - glosBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // Test now, because result may change after doBrowseDirectory(). - boolean isDefaultGlossaryFile = projectProperties.isDefaultWriteableGlossaryFile(); - doBrowseDirectoy(3, glosRootField); - // If file started as default, automatically use new default. - if (isDefaultGlossaryFile) { - String newDefault = projectProperties.computeDefaultWriteableGlossaryFile(); - projectProperties.setWriteableGlossary(newDefault); - writeableGlosField.setText(newDefault); - } - } - }); - - wGlosBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doBrowseDirectoy(6, writeableGlosField); - } - }); - - tmBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doBrowseDirectoy(4, tmRootField); - } - }); - - exportTMBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doBrowseDirectoy(7, exportTMRootField); - } - }); - - dictBrowse.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doBrowseDirectoy(5, dictRootField); - } - }); - - sentenceSegmentingButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - SegmentationCustomizer segmentationCustomizer = new SegmentationCustomizer(true, - SRX.getDefault(), Preferences.getSRX(), srx); - if (segmentationCustomizer.show(ProjectPropertiesDialog.this)) { - srx = segmentationCustomizer.getResult(); - } - } - }); - - fileFiltersButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - FiltersCustomizer dlg = new FiltersCustomizer(true, - FilterMaster.createDefaultFiltersConfig(), Preferences.getFilters(), filters); - if (dlg.show(ProjectPropertiesDialog.this)) { - // saving config - filters = dlg.getResult(); - } - } - }); - - repositoriesButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - List r = new RepositoriesMappingController().show(parent, - projectProperties.getRepositories()); - if (r != null) { - projectProperties.setRepositories(r); - } - } - }); - - externalFinderButton.addActionListener(e -> { - ExternalFinderCustomizer dlg = new ExternalFinderCustomizer(true, externalFinderConfig); - if (dlg.show(ProjectPropertiesDialog.this)) { - externalFinderConfig = dlg.getResult(); - } - }); - - StaticUIUtils.setEscapeAction(this, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - doCancel(); - } - }); - - srcRootField.setText(projectProperties.getSourceRoot()); - locRootField.setText(projectProperties.getTargetRoot()); - glosRootField.setText(projectProperties.getGlossaryRoot()); - writeableGlosField.setText(projectProperties.getWriteableGlossary()); - tmRootField.setText(projectProperties.getTMRoot()); - dictRootField.setText(projectProperties.getDictRoot()); - - exportTMRootField.setText(projectProperties.getExportTMRoot()); - exportTMOmegaTCheckBox.setSelected(projectProperties.isExportTm("omegat")); - exportTMLevel1CheckBox.setSelected(projectProperties.isExportTm("level1")); - exportTMLevel2CheckBox.setSelected(projectProperties.isExportTm("level2")); - - sentenceSegmentingCheckBox.setSelected(projectProperties.isSentenceSegmentingEnabled()); - allowDefaultsCheckBox.setSelected(projectProperties.isSupportDefaultTranslations()); - removeTagsCheckBox.setSelected(projectProperties.isRemoveTags()); - - switch (dialogType) { - case RESOLVE_DIRS: - // disabling some of the controls - sourceLocaleField.setEnabled(false); - targetLocaleField.setEnabled(false); - sourceTokenizerField.setEnabled(false); - targetTokenizerField.setEnabled(false); - sentenceSegmentingCheckBox.setEnabled(false); - allowDefaultsCheckBox.setEnabled(false); - removeTagsCheckBox.setEnabled(false); - externalCommandTextArea.setEnabled(false); - insertButton.setEnabled(false); - variablesList.setEnabled(false); - exportTMOmegaTCheckBox.setEnabled(false); - exportTMLevel1CheckBox.setEnabled(false); - exportTMLevel2CheckBox.setEnabled(false); - - // marking missing folder RED - File f = new File(srcRootField.getText()); - if (!f.isDirectory()) { - srcRootField.setForeground(Color.RED); - } - f = new File(locRootField.getText()); - if (!f.isDirectory()) { - locRootField.setForeground(Color.RED); - } - f = new File(glosRootField.getText()); - if (!f.isDirectory()) { - glosRootField.setForeground(Color.RED); - } - f = new File(writeableGlosField.getText()); - File wGlos = f.getParentFile(); // Remove the file name - // The writeable glossary must be in in the /glossary folder - if (!wGlos.isDirectory() || !wGlos.equals(new File(glosRootField.getText()))) { - writeableGlosField.setForeground(Color.RED); - } - f = new File(tmRootField.getText()); - if (!f.isDirectory()) { - tmRootField.setForeground(Color.RED); - } - f = new File(exportTMRootField.getText()); - if (!f.isDirectory()) { - exportTMRootField.setForeground(Color.RED); - } - f = new File(dictRootField.getText()); - if (!f.isDirectory()) { - dictRootField.setForeground(Color.RED); - } - break; - default: - break; - } - - // Pack once to get the width... - pack(); - updateUIText(); - // Then again to expand the height to accomodate the message. - // This is needed because the height isn't known until the - // amount of linewrapping is known. - pack(); - // The result is still slightly too small on some LAFs, so enlarge - // slightly with magic numbers. - setSize(9 * getWidth() / 8, getHeight() + 10); - setResizable(true); - - StaticUIUtils.fitInScreen(this); - setLocationRelativeTo(parent); - } - // CHECKSTYLE:ON - - /** - * Browses for the directory. - * - * @param browseTarget - * customizes the messages depending on what is browsed for - * @param field - * text field to write browsed folder to - */ - private void doBrowseDirectoy(int browseTarget, JTextField field) { - if (field == null) { - return; - } - String title; - boolean fileMode = false; - boolean glossaryFile = false; - - if (browseTarget == 6) { - fileMode = true; - glossaryFile = true; - } - - switch (browseTarget) { - case 1: - title = OStrings.getString("PP_BROWSE_TITLE_SOURCE"); - break; - - case 2: - title = OStrings.getString("PP_BROWSE_TITLE_TARGET"); - break; - - case 3: - title = OStrings.getString("PP_BROWSE_TITLE_GLOS"); - break; - - case 4: - title = OStrings.getString("PP_BROWSE_TITLE_TM"); - break; - - case 5: - title = OStrings.getString("PP_BROWSE_TITLE_DICT"); - break; - - case 6: - title = OStrings.getString("PP_BROWSE_W_GLOS"); - break; - - case 7: - title = OStrings.getString("PP_BROWSE_TITLE_EXPORT_TM"); - break; - default: - return; - } - - OmegaTFileChooser browser = new OmegaTFileChooser(); - browser.setDialogTitle(title); - if (fileMode) { - browser.setFileSelectionMode(OmegaTFileChooser.FILES_ONLY); - } else { - browser.setFileSelectionMode(OmegaTFileChooser.DIRECTORIES_ONLY); - } - - // check if the current directory as specified by the field exists - String curDir = field.getText(); - File curDirCheck = new File(curDir); - if (fileMode && !StringUtil.isEmpty(curDirCheck.getName())) { - String dirOnly = curDirCheck.getParent(); - dirOnly = (dirOnly != null) ? dirOnly : ""; - curDirCheck = new File(dirOnly); - } - - // if the dir doesn't exist, use project dir and check if that exists - if (!curDirCheck.exists() || !curDirCheck.isDirectory()) { - curDir = projectProperties.getProjectRoot(); - curDirCheck = new File(curDir); - } - - // if all fails, get last used dir from preferences - if (!curDirCheck.exists() || !curDirCheck.isDirectory()) { - switch (browseTarget) { - case 1: - curDir = Preferences.getPreference(Preferences.SOURCE_FOLDER); - break; - - case 2: - curDir = Preferences.getPreference(Preferences.TARGET_FOLDER); - break; - - case 3: - curDir = Preferences.getPreference(Preferences.GLOSSARY_FOLDER); - break; - - case 4: - curDir = Preferences.getPreference(Preferences.TM_FOLDER); - break; - - case 5: - curDir = Preferences.getPreference(Preferences.DICT_FOLDER); - break; - - case 6: - curDir = Preferences.getPreference(Preferences.GLOSSARY_FILE); - break; - - case 7: - curDir = Preferences.getPreference(Preferences.EXPORT_TM_FOLDER); - break; - - } - } - - if (fileMode) { - File dirFile = new File(curDir); - curDir = dirFile.getParent(); - } - - if (curDir.equals("")) { - curDir = Preferences.getPreference(Preferences.CURRENT_FOLDER); - } - - if (!curDir.equals("")) { - File dir = new File(curDir); - if (dir.exists() && dir.isDirectory()) { - browser.setCurrentDirectory(dir); - } - } - - // show the browser - int action = browser.showOpenDialog(this); - - // check if the selection has been approved - if (action != javax.swing.JFileChooser.APPROVE_OPTION) { - return; - } - - // get the selected folder - File dir = browser.getSelectedFile(); - if (dir == null) { - return; - } - - String str = dir.getAbsolutePath(); - if (!fileMode) { - str += File.separator; // Add file separator for directories - } - - // The writeable glossary file must end with .txt or utf8. Not .tab, because it not necessarily is .utf8 - if (glossaryFile && !str.endsWith(OConsts.EXT_TSV_TXT) && !str.endsWith(OConsts.EXT_TSV_UTF8)) { - str += OConsts.EXT_TSV_TXT; // Defaults to .txt - } - - // reset appropriate path - store preferred directory - switch (browseTarget) { - case 1: - Preferences.setPreference(Preferences.SOURCE_FOLDER, browser.getSelectedFile().getParent()); - projectProperties.setSourceRoot(str); - field.setText(projectProperties.getSourceRoot()); - if (new File(projectProperties.getSourceRoot()).exists() - && new File(projectProperties.getSourceRoot()).isDirectory()) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - - case 2: - Preferences.setPreference(Preferences.TARGET_FOLDER, browser.getSelectedFile().getParent()); - projectProperties.setTargetRoot(str); - field.setText(projectProperties.getTargetRoot()); - if (new File(projectProperties.getTargetRoot()).exists() - && new File(projectProperties.getTargetRoot()).isDirectory()) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - - case 3: - Preferences.setPreference(Preferences.GLOSSARY_FOLDER, browser.getSelectedFile().getParent()); - projectProperties.setGlossaryRoot(str); - field.setText(projectProperties.getGlossaryRoot()); - if (new File(projectProperties.getGlossaryRoot()).exists() - && new File(projectProperties.getGlossaryRoot()).isDirectory()) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - - case 4: - Preferences.setPreference(Preferences.TM_FOLDER, browser.getSelectedFile().getParent()); - projectProperties.setTMRoot(str); - field.setText(projectProperties.getTMRoot()); - if (new File(projectProperties.getTMRoot()).exists() - && new File(projectProperties.getTMRoot()).isDirectory()) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - - case 5: - Preferences.setPreference(Preferences.DICT_FOLDER, browser.getSelectedFile().getParent()); - projectProperties.setDictRoot(str); - field.setText(projectProperties.getDictRoot()); - if (new File(projectProperties.getDictRoot()).exists() - && new File(projectProperties.getDictRoot()).isDirectory()) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - - case 6: - Preferences.setPreference(Preferences.GLOSSARY_FILE, browser.getSelectedFile().getPath()); - projectProperties.setWriteableGlossary(str); - field.setText(projectProperties.getWriteableGlossary()); - // The writable glosssary file must be inside the glossary dir - if (new File(projectProperties.getWriteableGlossaryDir()).exists() - && new File(projectProperties.getWriteableGlossaryDir()).isDirectory() - && projectProperties.getWriteableGlossaryDir().contains(projectProperties.getGlossaryRoot())) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - - case 7: - Preferences.setPreference(Preferences.EXPORT_TM_FOLDER, browser.getSelectedFile().getParent()); - projectProperties.setExportTMRoot(str); - field.setText(projectProperties.getExportTMRoot()); - if (new File(projectProperties.getExportTMRoot()).exists() - && new File(projectProperties.getExportTMRoot()).isDirectory()) { - field.setForeground(java.awt.SystemColor.textText); - } - break; - } - } - - private void doOK(JComboBox sourceLocaleField, JComboBox targetLocaleField, - JComboBox> sourceTokenizerField, JComboBox> targetTokenizerField, - JCheckBox sentenceSegmentingCheckBox, JTextField srcRootField, JTextField locRootField, - JTextField glosRootField, JTextField writeableGlosField, JTextField tmRootField, - JTextField exportTMRootField, JCheckBox exportTMOmegaTCheckBox, JCheckBox exportTMLevel1CheckBox, - JCheckBox exportTMLevel2CheckBox, JTextField dictRootField, JCheckBox allowDefaultsCheckBox, - JCheckBox removeTagsCheckBox, JTextArea customCommandTextArea) { - if (!Language.verifySingleLangCode(sourceLocaleField.getSelectedItem().toString())) { - JOptionPane.showMessageDialog( - this, - OStrings.getString("NP_INVALID_SOURCE_LOCALE") - + OStrings.getString("NP_LOCALE_SUGGESTION"), OStrings.getString("TF_ERROR"), - JOptionPane.ERROR_MESSAGE); - sourceLocaleField.requestFocusInWindow(); - return; - } - projectProperties.setSourceLanguage(sourceLocaleField.getSelectedItem().toString()); - - if (!Language.verifySingleLangCode(targetLocaleField.getSelectedItem().toString())) { - JOptionPane.showMessageDialog( - this, - OStrings.getString("NP_INVALID_TARGET_LOCALE") - + OStrings.getString("NP_LOCALE_SUGGESTION"), OStrings.getString("TF_ERROR"), - JOptionPane.ERROR_MESSAGE); - targetLocaleField.requestFocusInWindow(); - return; - } - projectProperties.setTargetLanguage(targetLocaleField.getSelectedItem().toString()); - - if (sourceTokenizerField.isEnabled()) { - projectProperties.setSourceTokenizer((Class) sourceTokenizerField.getSelectedItem()); - } - - if (targetTokenizerField.isEnabled()) { - projectProperties.setTargetTokenizer((Class) targetTokenizerField.getSelectedItem()); - } - - projectProperties.setSentenceSegmentingEnabled(sentenceSegmentingCheckBox.isSelected()); - - projectProperties.setSupportDefaultTranslations(allowDefaultsCheckBox.isSelected()); - - projectProperties.setRemoveTags(removeTagsCheckBox.isSelected()); - - projectProperties.setExportTmLevels(exportTMOmegaTCheckBox.isSelected(), - exportTMLevel1CheckBox.isSelected(), - exportTMLevel2CheckBox.isSelected()); - - projectProperties.setExternalCommand(customCommandTextArea.getText()); - - projectProperties.setSourceRoot(srcRootField.getText()); - if (!projectProperties.getSourceRoot().endsWith(File.separator)) { - projectProperties.setSourceRoot(projectProperties.getSourceRoot() + File.separator); - } - - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getSourceRoot()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_SOURCEDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - srcRootField.requestFocusInWindow(); - return; - } - - projectProperties.setTargetRoot(locRootField.getText()); - if (!projectProperties.getTargetRoot().endsWith(File.separator)) { - projectProperties.setTargetRoot(projectProperties.getTargetRoot() + File.separator); - } - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getTargetRoot()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_TRANSDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - locRootField.requestFocusInWindow(); - return; - } - - projectProperties.setGlossaryRoot(glosRootField.getText()); - if (!projectProperties.getGlossaryRoot().endsWith(File.separator)) { - projectProperties.setGlossaryRoot(projectProperties.getGlossaryRoot() + File.separator); - } - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getGlossaryRoot()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_GLOSSDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - glosRootField.requestFocusInWindow(); - return; - } - - projectProperties.setWriteableGlossary(writeableGlosField.getText()); - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getWriteableGlossaryDir()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_W_GLOSSDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - writeableGlosField.requestFocusInWindow(); - return; - } - - String glossaryDir = projectProperties.getWriteableGlossaryDir(); - if (!glossaryDir.endsWith(File.separator)) { - glossaryDir += File.separator; - } - if (!glossaryDir.contains(projectProperties.getGlossaryRoot())) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_W_GLOSDIR_NOT_INSIDE_GLOS"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - writeableGlosField.requestFocusInWindow(); - return; - } - - projectProperties.setTMRoot(tmRootField.getText()); - if (!projectProperties.getTMRoot().endsWith(File.separator)) { - projectProperties.setTMRoot(projectProperties.getTMRoot() + File.separator); - } - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getTMRoot()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_TMDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - tmRootField.requestFocusInWindow(); - return; - } - - projectProperties.setExportTMRoot(exportTMRootField.getText()); - if (!projectProperties.getExportTMRoot().endsWith(File.separator)) { - projectProperties.setExportTMRoot(projectProperties.getExportTMRoot() + File.separator); - } - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getExportTMRoot()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_EXPORT_TMDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - exportTMRootField.requestFocusInWindow(); - return; - } - - projectProperties.setDictRoot(dictRootField.getText()); - if (!projectProperties.getDictRoot().endsWith(File.separator)) { - projectProperties.setDictRoot(projectProperties.getDictRoot() + File.separator); - } - if (dialogType != Mode.NEW_PROJECT && !new File(projectProperties.getDictRoot()).exists()) { - JOptionPane.showMessageDialog(this, OStrings.getString("NP_DICTDIR_DOESNT_EXIST"), - OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); - dictRootField.requestFocusInWindow(); - return; - } - - projectProperties.setExportTmLevels(exportTMOmegaTCheckBox.isSelected(), exportTMLevel1CheckBox.isSelected(), - exportTMLevel2CheckBox.isSelected()); - - projectProperties.setProjectSRX(srx); - projectProperties.setProjectFilters(filters); - projectProperties.getSourceRootExcludes().clear(); - projectProperties.getSourceRootExcludes().addAll(srcExcludes); - - ExternalFinder.setProjectConfig(externalFinderConfig); - - dialogCancelled = false; - setVisible(false); + return dirsBox; } - private void doCancel() { - // delete project dir in case of a new project - // to fix bug 1476591 the project root is created before everything else - // and if the new project is cancelled, the project root still exists, - // so it must be deleted - if (dialogType == Mode.NEW_PROJECT) { - new File(projectProperties.getProjectRoot()).delete(); + private void setResolveDirsDefaults() { + // disabling some controls + sourceLocaleField.setEnabled(false); + targetLocaleField.setEnabled(false); + sourceTokenizerField.setEnabled(false); + targetTokenizerField.setEnabled(false); + sentenceSegmentingCheckBox.setEnabled(false); + allowDefaultsCheckBox.setEnabled(false); + removeTagsCheckBox.setEnabled(false); + externalCommandTextArea.setEnabled(false); + insertButton.setEnabled(false); + variablesList.setEnabled(false); + exportTMOmegaTCheckBox.setEnabled(false); + exportTMLevel1CheckBox.setEnabled(false); + exportTMLevel2CheckBox.setEnabled(false); + + // marking missing folder RED + File f = new File(srcRootField.getText()); + if (!f.isDirectory()) { + srcRootField.setForeground(Color.RED); + } + f = new File(locRootField.getText()); + if (!f.isDirectory()) { + locRootField.setForeground(Color.RED); + } + f = new File(glosRootField.getText()); + if (!f.isDirectory()) { + glosRootField.setForeground(Color.RED); + } + f = new File(writeableGlosField.getText()); + File wGlos = f.getParentFile(); // Remove the file name + // The writeable glossary must be in in the /glossary folder + if (!wGlos.isDirectory() || !wGlos.equals(new File(glosRootField.getText()))) { + writeableGlosField.setForeground(Color.RED); + } + f = new File(tmRootField.getText()); + if (!f.isDirectory()) { + tmRootField.setForeground(Color.RED); + } + f = new File(exportTMRootField.getText()); + if (!f.isDirectory()) { + exportTMRootField.setForeground(Color.RED); + } + f = new File(dictRootField.getText()); + if (!f.isDirectory()) { + dictRootField.setForeground(Color.RED); } - dialogCancelled = true; - setVisible(false); } private void updateUIText() { @@ -1251,15 +663,96 @@ public boolean getScrollableTracksViewportHeight() { } - /** - * Whether the user cancelled the dialog. - */ - private boolean dialogCancelled; - /** * Return new properties or null if dialog cancelled. */ public ProjectProperties getResult() { - return dialogCancelled ? null : projectProperties; + return controller.getResult(); + + } + + Mode getDialogType() { + return dialogType; } + + // multiple translations + JCheckBox allowDefaultsCheckBox = new JCheckBox(); + + // Remove Tags + JCheckBox removeTagsCheckBox = new JCheckBox(); + JButton exportTMBrowse = new JButton(); + JButton sentenceSegmentingButton = new JButton(); + + // File Filters + JButton fileFiltersButton = new JButton(); + JTextField exportTMRootField = new JTextField(); + JCheckBox exportTMOmegaTCheckBox; + JCheckBox exportTMLevel1CheckBox; + JCheckBox exportTMLevel2CheckBox; + JButton srcExcludesBtn = new JButton(); + JTextField srcRootField = new JTextField(); + JTextField tmRootField = new JTextField(); + JTextField locRootField = new JTextField(); + + // sentence-segmenting + JCheckBox sentenceSegmentingCheckBox = new JCheckBox(); + JButton okButton = new JButton(); + JButton wGlosBrowse = new JButton(); + JButton srcBrowse = new JButton(); + JButton tmBrowse = new JButton(); + JButton insertButton = new JButton(); + JButton cancelButton = new JButton(); + JComboBox> targetTokenizerField; + JComboBox targetLocaleField; + JComboBox> sourceTokenizerField; + JComboBox sourceLocaleField; + + // Repositories mapping + JButton repositoriesButton = new JButton(); + + // Repositories mapping + JButton externalFinderButton = new JButton(); + + JTextField glosRootField = new JTextField(); + JTextField writeableGlosField = new JTextField(); + JTextField dictRootField = new JTextField(); + JTextArea externalCommandTextArea = new JTextArea(); + JButton locBrowse = new JButton(); + JButton glosBrowse = new JButton(); + JButton dictBrowse = new JButton(); + + // extern command + JLabel variablesLabel = new javax.swing.JLabel(); + JComboBox variablesList; + + // component name definitions for ui test. + public static final String DIALOG_NAME = "project_properties_dialog"; + public static final String OK_BUTTON_NAME = "project_properties_ok_button"; + public static final String CANCEL_BUTTON_NAME = "project_properties_cancel_button"; + public static final String SENTENCE_SEGMENTING_CB_NAME = "project_properties_sentence_segmenting_cb"; + public static final String SENTENCE_SEGMENTING_BUTTON_NAME = "project_properties_sentence_segmenting_button"; + public static final String ALLOW_DEFAULTS_CB_NAME = "project_properties_allow_defaults_cb"; + public static final String REMOVE_TAGS_CB_NAME = "project_properties_remove_tags_cb"; + public static final String EXPORT_TM_BROWSE_BUTTON_NAME = "project_properties_export_tm_browse_button"; + public static final String FILE_FILTER_BUTTON_NAME = "project_properties_file_filter_button"; + public static final String EXPORT_TM_ROOT_FIELD_NAME = "project_properties_export_tm_root_field"; + public static final String EXPORT_TM_OMEGAT_CB_NAME = "project_properties_export_tm_omegat_cb"; + public static final String EXPORT_TM_LEVEL1_CB_NAME = "project_properties_export_tm_level1_cb"; + public static final String EXPORT_TM_LEVEL2_CB_NAME = "project_properties_export_tm_level2_cb"; + public static final String REPOSITORIES_BUTTON_NAME = "project_properties_repositories_button"; + public static final String EXTERNAL_FINDER_BUTTON_NAME = "project_properties_external_finder_button"; + public static final String EXTERNAL_COMMAND_TEXTAREA_NAME = + "project_properties_external_command_textarea"; + public static final String VARIABLE_LIST_NAME = "project_properties_variable_list"; + public static final String SRC_EXCLUDES_BUTTON_NAME = "project_properties_src_excludes_button"; + public static final String TM_BROWSE_BUTTON_NAME = "project_properties_tm_browse"; + public static final String GLOSSARY_BROWSE_BUTTON_NAME = "project_properties_glossary_browse_button"; + public static final String WRITABLE_GLOSSARY_BROWSE_BUTTON_NAME = + "project_properties_writable_glossary_browse_button"; + public static final String LOC_BROWSE_BUTTON_NAME = "project_properties_loc_browse_button"; + public static final String DICTIONARY_BROWSE_BUTTON_NAME = "project_properties_dictionary_browse_button"; + public static final String SOURCE_TOKENIZER_FIELD_NAME = "project_properties_source_tokenizer_field"; + public static final String TARGET_TOKENIZER_FIELD_NAME = "project_properties_target_tokenizer_field"; + public static final String SOURCE_LOCALE_CB_NAME = "project_properties_source_locale_cb"; + public static final String TARGET_LOCALE_CB_NAME = "project_properties_target_locale_cb"; } diff --git a/src/org/omegat/gui/dialogs/ProjectPropertiesDialogController.java b/src/org/omegat/gui/dialogs/ProjectPropertiesDialogController.java new file mode 100644 index 0000000000..a14649c0f0 --- /dev/null +++ b/src/org/omegat/gui/dialogs/ProjectPropertiesDialogController.java @@ -0,0 +1,603 @@ +/************************************************************************** + OmegaT - Computer Assisted Translation (CAT) tool + with fuzzy matching, translation memory, keyword search, + glossaries, and translation leveraging into updated projects. + + Copyright (C) 2000-2006 Keith Godfrey and Maxym Mykhalchuk + 2008-2009 Alex Buloichik + 2011 Martin Fleurke + 2012 Didier Briel, Aaron Madlon-Kay + 2013 Aaron Madlon-Kay, Yu Tang + 2014-2015 Aaron Madlon-Kay + 2024 Hiroshi Miura + Home page: https://www.omegat.org/ + Support center: https://omegat.org/support + + This file is part of OmegaT. + + OmegaT is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OmegaT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **************************************************************************/ + +package org.omegat.gui.dialogs; + +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.JTextField; + +import org.omegat.core.data.ProjectProperties; +import org.omegat.core.segmentation.SRX; +import org.omegat.externalfinder.ExternalFinder; +import org.omegat.externalfinder.gui.ExternalFinderCustomizer; +import org.omegat.externalfinder.item.ExternalFinderConfiguration; +import org.omegat.filters2.master.FilterMaster; +import org.omegat.filters2.master.PluginUtils; +import org.omegat.gui.filters2.FiltersCustomizer; +import org.omegat.gui.segmentation.SegmentationCustomizer; +import org.omegat.util.Language; +import org.omegat.util.OConsts; +import org.omegat.util.OStrings; +import org.omegat.util.Preferences; +import org.omegat.util.StringUtil; +import org.omegat.util.gui.OmegaTFileChooser; +import org.omegat.util.gui.StaticUIUtils; + +import gen.core.filters.Filters; +import gen.core.project.RepositoryDefinition; + +public class ProjectPropertiesDialogController { + + private final ProjectProperties projectProperties; + + /** Project SRX. */ + private SRX srx; + + /** Project filters. */ + private Filters filters; + + /** Project ExternalFinder config */ + private ExternalFinderConfiguration externalFinderConfig; + + private final List srcExcludes = new ArrayList<>(); + + /** + * Whether the user canceled the dialog. + */ + private boolean dialogCancelled; + + private final Frame parent; + private final ProjectPropertiesDialog dialog; + + public ProjectPropertiesDialogController(Frame parent, ProjectPropertiesDialog dialog, + ProjectPropertiesDialog.Mode type, ProjectProperties projectProperties) { + this.parent = parent; + this.dialog = dialog; + this.projectProperties = projectProperties; + this.srx = projectProperties.getProjectSRX(); + this.filters = projectProperties.getProjectFilters(); + srcExcludes.addAll(projectProperties.getSourceRootExcludes()); + externalFinderConfig = ExternalFinder.getProjectConfig(); + initFromProperties(); + initializeActions(type); + } + + /** + * Return new properties or null if dialog cancelled. + */ + public ProjectProperties getResult() { + return dialogCancelled ? null : projectProperties; + } + + private void initFromProperties() { + dialog.srcRootField.setText(projectProperties.getSourceRoot()); + dialog.locRootField.setText(projectProperties.getTargetRoot()); + dialog.glosRootField.setText(projectProperties.getGlossaryRoot()); + dialog.writeableGlosField.setText(projectProperties.getWriteableGlossary()); + dialog.tmRootField.setText(projectProperties.getTMRoot()); + dialog.dictRootField.setText(projectProperties.getDictRoot()); + + dialog.exportTMRootField.setText(projectProperties.getExportTMRoot()); + dialog.exportTMOmegaTCheckBox.setSelected(projectProperties.isExportTm("omegat")); + dialog.exportTMLevel1CheckBox.setSelected(projectProperties.isExportTm("level1")); + dialog.exportTMLevel2CheckBox.setSelected(projectProperties.isExportTm("level2")); + + dialog.sentenceSegmentingCheckBox.setSelected(projectProperties.isSentenceSegmentingEnabled()); + dialog.allowDefaultsCheckBox.setSelected(projectProperties.isSupportDefaultTranslations()); + dialog.removeTagsCheckBox.setSelected(projectProperties.isRemoveTags()); + + dialog.sourceLocaleField.setSelectedItem(projectProperties.getSourceLanguage()); + dialog.targetLocaleField.setSelectedItem(projectProperties.getTargetLanguage()); + + dialog.sourceTokenizerField.setSelectedItem(projectProperties.getSourceTokenizer()); + dialog.targetTokenizerField.setSelectedItem(projectProperties.getTargetTokenizer()); + + dialog.externalCommandTextArea.setText(projectProperties.getExternalCommand()); + } + + private void initializeActions(ProjectPropertiesDialog.Mode dialogType) { + dialog.okButton.addActionListener(e -> doOK()); + StaticUIUtils.setEscapeAction(dialog, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + doCancel(); + } + }); + + // Configure locale actions and initialize + ActionListener sourceLocaleListener = e -> { + if (!dialog.sourceLocaleField.isEnabled()) { + return; + } + Object newLang = dialog.sourceLocaleField.getSelectedItem(); + if (newLang instanceof String) { + newLang = new Language((String) newLang); + } + Class newTok = PluginUtils.getTokenizerClassForLanguage((Language) newLang); + dialog.sourceTokenizerField.setSelectedItem(newTok); + }; + dialog.sourceLocaleField.addActionListener(sourceLocaleListener); + ActionListener targetLocaleListener = e -> { + if (!dialog.targetLocaleField.isEnabled()) { + return; + } + Object newLang = dialog.targetLocaleField.getSelectedItem(); + if (newLang instanceof String) { + newLang = new Language((String) newLang); + } + Class newTok = PluginUtils.getTokenizerClassForLanguage((Language) newLang); + dialog.targetTokenizerField.setSelectedItem(newTok); + }; + dialog.targetLocaleField.addActionListener(targetLocaleListener); + if (dialogType == ProjectPropertiesDialog.Mode.NEW_PROJECT) { + // Infer appropriate tokenizers from source languages + sourceLocaleListener.actionPerformed(null); + targetLocaleListener.actionPerformed(null); + } + + dialog.repositoriesButton.addActionListener(e -> { + List r = new RepositoriesMappingController().show(parent, + projectProperties.getRepositories()); + if (r != null) { + projectProperties.setRepositories(r); + } + }); + dialog.externalFinderButton.addActionListener(e -> { + ExternalFinderCustomizer dlg = new ExternalFinderCustomizer(true, externalFinderConfig); + if (dlg.show(dialog)) { + externalFinderConfig = dlg.getResult(); + } + }); + dialog.cancelButton.addActionListener(e -> doCancel()); + dialog.srcBrowse.addActionListener(e -> doBrowseDirectory(1, dialog.srcRootField)); + dialog.srcExcludesBtn.addActionListener(e -> { + List result = FilenamePatternsEditorController.show(srcExcludes); + if (result != null) { + srcExcludes.clear(); + srcExcludes.addAll(result); + } + }); + dialog.locBrowse.addActionListener(e -> doBrowseDirectory(2, dialog.locRootField)); + dialog.glosBrowse.addActionListener(e -> { + // Test now, because a result may change after doBrowseDirectory(). + boolean isDefaultGlossaryFile = projectProperties.isDefaultWriteableGlossaryFile(); + doBrowseDirectory(3, dialog.glosRootField); + // If a file started as default, automatically use new default. + if (isDefaultGlossaryFile) { + String newDefault = projectProperties.computeDefaultWriteableGlossaryFile(); + projectProperties.setWriteableGlossary(newDefault); + dialog.writeableGlosField.setText(newDefault); + } + }); + dialog.wGlosBrowse.addActionListener(e -> doBrowseDirectory(6, dialog.writeableGlosField)); + dialog.tmBrowse.addActionListener(e -> doBrowseDirectory(4, dialog.tmRootField)); + dialog.exportTMBrowse.addActionListener(e -> doBrowseDirectory(7, dialog.exportTMRootField)); + dialog.dictBrowse.addActionListener(e -> doBrowseDirectory(5, dialog.dictRootField)); + dialog.sentenceSegmentingButton.addActionListener(e -> { + SegmentationCustomizer segmentationCustomizer = new SegmentationCustomizer(true, SRX.getDefault(), + Preferences.getSRX(), srx); + if (segmentationCustomizer.show(dialog)) { + srx = segmentationCustomizer.getResult(); + } + }); + dialog.fileFiltersButton.addActionListener(e -> { + FiltersCustomizer dlg = new FiltersCustomizer(true, FilterMaster.createDefaultFiltersConfig(), + Preferences.getFilters(), filters); + if (dlg.show(dialog)) { + filters = dlg.getResult(); // saving config + } + }); + } + + /** + * Browses for the directory. + * + * @param browseTarget + * customizes the messages depending on what is browsed for + * @param field + * text field to write browsed folder to + */ + private void doBrowseDirectory(int browseTarget, JTextField field) { + if (field == null) { + return; + } + boolean fileMode = false; + boolean glossaryFile = false; + + if (browseTarget == 6) { + fileMode = true; + glossaryFile = true; + } + String title = getBrowserTitle(browseTarget); + if (title == null) { + return; + } + + OmegaTFileChooser browser = new OmegaTFileChooser(); + browser.setDialogTitle(title); + if (fileMode) { + browser.setFileSelectionMode(OmegaTFileChooser.FILES_ONLY); + } else { + browser.setFileSelectionMode(OmegaTFileChooser.DIRECTORIES_ONLY); + } + + // check if the current directory as specified by the field exists + setCurDir(browser, field, browseTarget, fileMode); + + // show the browser + int action = browser.showOpenDialog(dialog); + + // check if the selection has been approved + if (action != javax.swing.JFileChooser.APPROVE_OPTION) { + return; + } + + // get the selected folder + File dir = browser.getSelectedFile(); + if (dir == null) { + return; + } + + String str = dir.getAbsolutePath(); + if (!fileMode) { + str += File.separator; // Add file separator for directories + } + + // The writeable glossary file must end with .txt or utf8. Not .tab, + // because it not necessarily is .utf8 + if (glossaryFile && !str.endsWith(OConsts.EXT_TSV_TXT) && !str.endsWith(OConsts.EXT_TSV_UTF8)) { + str += OConsts.EXT_TSV_TXT; // Defaults to .txt + } + + resetThePathAndWarn(browser, field, browseTarget, str); + } + + private String getBrowserTitle(int browseTarget) { + switch (browseTarget) { + case 1: + return OStrings.getString("PP_BROWSE_TITLE_SOURCE"); + case 2: + return OStrings.getString("PP_BROWSE_TITLE_TARGET"); + case 3: + return OStrings.getString("PP_BROWSE_TITLE_GLOS"); + case 4: + return OStrings.getString("PP_BROWSE_TITLE_TM"); + case 5: + return OStrings.getString("PP_BROWSE_TITLE_DICT"); + case 6: + return OStrings.getString("PP_BROWSE_W_GLOS"); + case 7: + return OStrings.getString("PP_BROWSE_TITLE_EXPORT_TM"); + default: + return null; + } + } + + private void setCurDir(OmegaTFileChooser browser, JTextField field, int browseTarget, boolean fileMode) { + String curDir = field.getText(); + File curDirCheck = new File(curDir); + if (fileMode && !StringUtil.isEmpty(curDirCheck.getName())) { + String dirOnly = curDirCheck.getParent(); + dirOnly = (dirOnly != null) ? dirOnly : ""; + curDirCheck = new File(dirOnly); + } + + // if the dir doesn't exist, use project dir and check if that exists + if (!curDirCheck.exists() || !curDirCheck.isDirectory()) { + curDir = projectProperties.getProjectRoot(); + curDirCheck = new File(curDir); + } + + // if all fails, get last used dir from preferences + if (!curDirCheck.exists() || !curDirCheck.isDirectory()) { + curDir = getDirFromPreference(browseTarget); + } + + if (fileMode) { + File dirFile = new File(curDir); + curDir = dirFile.getParent(); + } + + if (curDir.isEmpty()) { + curDir = Preferences.getPreference(Preferences.CURRENT_FOLDER); + } + + if (!curDir.isEmpty()) { + File dir = new File(curDir); + if (dir.exists() && dir.isDirectory()) { + browser.setCurrentDirectory(dir); + } + } + } + + private String getDirFromPreference(int browseTarget) { + switch (browseTarget) { + case 1: + return Preferences.getPreference(Preferences.SOURCE_FOLDER); + case 2: + return Preferences.getPreference(Preferences.TARGET_FOLDER); + case 3: + return Preferences.getPreference(Preferences.GLOSSARY_FOLDER); + case 4: + return Preferences.getPreference(Preferences.TM_FOLDER); + case 5: + return Preferences.getPreference(Preferences.DICT_FOLDER); + case 6: + return Preferences.getPreference(Preferences.GLOSSARY_FILE); + case 7: + return Preferences.getPreference(Preferences.EXPORT_TM_FOLDER); + default: + return null; + } + } + + private void resetThePathAndWarn(OmegaTFileChooser browser, JTextField field, int browseTarget, + String str) { + // reset the appropriate path - store preferred directory + switch (browseTarget) { + case 1: + Preferences.setPreference(Preferences.SOURCE_FOLDER, browser.getSelectedFile().getParent()); + projectProperties.setSourceRoot(str); + field.setText(projectProperties.getSourceRoot()); + if (new File(projectProperties.getSourceRoot()).exists() + && new File(projectProperties.getSourceRoot()).isDirectory()) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + case 2: + Preferences.setPreference(Preferences.TARGET_FOLDER, browser.getSelectedFile().getParent()); + projectProperties.setTargetRoot(str); + field.setText(projectProperties.getTargetRoot()); + if (new File(projectProperties.getTargetRoot()).exists() + && new File(projectProperties.getTargetRoot()).isDirectory()) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + case 3: + Preferences.setPreference(Preferences.GLOSSARY_FOLDER, browser.getSelectedFile().getParent()); + projectProperties.setGlossaryRoot(str); + field.setText(projectProperties.getGlossaryRoot()); + if (new File(projectProperties.getGlossaryRoot()).exists() + && new File(projectProperties.getGlossaryRoot()).isDirectory()) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + case 4: + Preferences.setPreference(Preferences.TM_FOLDER, browser.getSelectedFile().getParent()); + projectProperties.setTMRoot(str); + field.setText(projectProperties.getTMRoot()); + if (new File(projectProperties.getTMRoot()).exists() + && new File(projectProperties.getTMRoot()).isDirectory()) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + case 5: + Preferences.setPreference(Preferences.DICT_FOLDER, browser.getSelectedFile().getParent()); + projectProperties.setDictRoot(str); + field.setText(projectProperties.getDictRoot()); + if (new File(projectProperties.getDictRoot()).exists() + && new File(projectProperties.getDictRoot()).isDirectory()) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + case 6: + Preferences.setPreference(Preferences.GLOSSARY_FILE, browser.getSelectedFile().getPath()); + projectProperties.setWriteableGlossary(str); + field.setText(projectProperties.getWriteableGlossary()); + // The writable glosssary file must be inside the glossary dir + if (new File(projectProperties.getWriteableGlossaryDir()).exists() + && new File(projectProperties.getWriteableGlossaryDir()).isDirectory() + && projectProperties.getWriteableGlossaryDir() + .contains(projectProperties.getGlossaryRoot())) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + case 7: + Preferences.setPreference(Preferences.EXPORT_TM_FOLDER, browser.getSelectedFile().getParent()); + projectProperties.setExportTMRoot(str); + field.setText(projectProperties.getExportTMRoot()); + if (new File(projectProperties.getExportTMRoot()).exists() + && new File(projectProperties.getExportTMRoot()).isDirectory()) { + field.setForeground(java.awt.SystemColor.textText); + } + break; + } + } + + private void doOK() { + if (!Language.verifySingleLangCode(dialog.sourceLocaleField.getSelectedItem().toString())) { + JOptionPane.showMessageDialog(dialog, + OStrings.getString("NP_INVALID_SOURCE_LOCALE") + + OStrings.getString("NP_LOCALE_SUGGESTION"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.sourceLocaleField.requestFocusInWindow(); + return; + } + projectProperties.setSourceLanguage(dialog.sourceLocaleField.getSelectedItem().toString()); + if (!Language.verifySingleLangCode(dialog.targetLocaleField.getSelectedItem().toString())) { + JOptionPane.showMessageDialog(dialog, + OStrings.getString("NP_INVALID_TARGET_LOCALE") + + OStrings.getString("NP_LOCALE_SUGGESTION"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.targetLocaleField.requestFocusInWindow(); + return; + } + projectProperties.setTargetLanguage(dialog.targetLocaleField.getSelectedItem().toString()); + if (dialog.sourceTokenizerField.isEnabled()) { + projectProperties.setSourceTokenizer((Class) dialog.sourceTokenizerField.getSelectedItem()); + } + if (dialog.targetTokenizerField.isEnabled()) { + projectProperties.setTargetTokenizer((Class) dialog.targetTokenizerField.getSelectedItem()); + } + + projectProperties.setSentenceSegmentingEnabled(dialog.sentenceSegmentingCheckBox.isSelected()); + projectProperties.setSupportDefaultTranslations(dialog.allowDefaultsCheckBox.isSelected()); + projectProperties.setRemoveTags(dialog.removeTagsCheckBox.isSelected()); + projectProperties.setExportTmLevels(dialog.exportTMOmegaTCheckBox.isSelected(), + dialog.exportTMLevel1CheckBox.isSelected(), dialog.exportTMLevel2CheckBox.isSelected()); + projectProperties.setExternalCommand(dialog.externalCommandTextArea.getText()); + projectProperties.setSourceRoot(dialog.srcRootField.getText()); + if (!projectProperties.getSourceRoot().endsWith(File.separator)) { + projectProperties.setSourceRoot(projectProperties.getSourceRoot() + File.separator); + } + + if (!isModeNewProject() && !new File(projectProperties.getSourceRoot()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_SOURCEDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.srcRootField.requestFocusInWindow(); + return; + } + + projectProperties.setTargetRoot(dialog.locRootField.getText()); + if (!projectProperties.getTargetRoot().endsWith(File.separator)) { + projectProperties.setTargetRoot(projectProperties.getTargetRoot() + File.separator); + } + if (!isModeNewProject() && !new File(projectProperties.getTargetRoot()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_TRANSDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.locRootField.requestFocusInWindow(); + return; + } + + projectProperties.setGlossaryRoot(dialog.glosRootField.getText()); + if (!projectProperties.getGlossaryRoot().endsWith(File.separator)) { + projectProperties.setGlossaryRoot(projectProperties.getGlossaryRoot() + File.separator); + } + if (!isModeNewProject() + && !new File(projectProperties.getGlossaryRoot()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_GLOSSDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.glosRootField.requestFocusInWindow(); + return; + } + + projectProperties.setWriteableGlossary(dialog.writeableGlosField.getText()); + if (!isModeNewProject() && !new File(projectProperties.getWriteableGlossaryDir()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_W_GLOSSDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.writeableGlosField.requestFocusInWindow(); + return; + } + + String glossaryDir = projectProperties.getWriteableGlossaryDir(); + if (!glossaryDir.endsWith(File.separator)) { + glossaryDir += File.separator; + } + if (!glossaryDir.contains(projectProperties.getGlossaryRoot())) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_W_GLOSDIR_NOT_INSIDE_GLOS"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.writeableGlosField.requestFocusInWindow(); + return; + } + + projectProperties.setTMRoot(dialog.tmRootField.getText()); + if (!projectProperties.getTMRoot().endsWith(File.separator)) { + projectProperties.setTMRoot(projectProperties.getTMRoot() + File.separator); + } + if (!isModeNewProject() && !new File(projectProperties.getTMRoot()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_TMDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.tmRootField.requestFocusInWindow(); + return; + } + + projectProperties.setExportTMRoot(dialog.exportTMRootField.getText()); + if (!projectProperties.getExportTMRoot().endsWith(File.separator)) { + projectProperties.setExportTMRoot(projectProperties.getExportTMRoot() + File.separator); + } + if (!isModeNewProject() && !new File(projectProperties.getExportTMRoot()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_EXPORT_TMDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.exportTMRootField.requestFocusInWindow(); + return; + } + + projectProperties.setDictRoot(dialog.dictRootField.getText()); + if (!projectProperties.getDictRoot().endsWith(File.separator)) { + projectProperties.setDictRoot(projectProperties.getDictRoot() + File.separator); + } + if (!isModeNewProject() && !new File(projectProperties.getDictRoot()).exists()) { + JOptionPane.showMessageDialog(dialog, OStrings.getString("NP_DICTDIR_DOESNT_EXIST"), + OStrings.getString("TF_ERROR"), JOptionPane.ERROR_MESSAGE); + dialog.dictRootField.requestFocusInWindow(); + return; + } + + projectProperties.setExportTmLevels(dialog.exportTMOmegaTCheckBox.isSelected(), + dialog.exportTMLevel1CheckBox.isSelected(), dialog.exportTMLevel2CheckBox.isSelected()); + + projectProperties.setProjectSRX(srx); + projectProperties.setProjectFilters(filters); + projectProperties.getSourceRootExcludes().clear(); + projectProperties.getSourceRootExcludes().addAll(srcExcludes); + + ExternalFinder.setProjectConfig(externalFinderConfig); + + dialogCancelled = false; + dialog.setVisible(false); + } + + private boolean isModeNewProject() { + return dialog.getDialogType() == ProjectPropertiesDialog.Mode.NEW_PROJECT; + } + + private void doCancel() { + // delete project dir in case of a new project + // to fix bug 1476591 the project root is created before everything else + // and if the new project is cancelled, the project root still exists, + // so it must be deleted + if (isModeNewProject()) { + new File(projectProperties.getProjectRoot()).delete(); + } + dialogCancelled = true; + dialog.setVisible(false); + } + + public static ProjectProperties showDialog(Frame parent, ProjectProperties projectProperties, + String projFileName, ProjectPropertiesDialog.Mode dialogTypeValue) { + if (dialogTypeValue == null) { + throw new RuntimeException("Unexpected null argument"); + } + ProjectPropertiesDialog dialog = new ProjectPropertiesDialog(parent, projectProperties, projFileName, + dialogTypeValue); + dialog.setVisible(true); + dialog.dispose(); + return dialog.getResult(); + } +} diff --git a/src/org/omegat/gui/main/ProjectUICommands.java b/src/org/omegat/gui/main/ProjectUICommands.java index 2b2215f443..0d1be7af63 100644 --- a/src/org/omegat/gui/main/ProjectUICommands.java +++ b/src/org/omegat/gui/main/ProjectUICommands.java @@ -51,6 +51,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.builder.EqualsBuilder; + import org.omegat.CLIParameters; import org.omegat.convert.ConvertProject; import org.omegat.core.Core; @@ -69,6 +70,7 @@ import org.omegat.gui.dialogs.NewProjectFileChooser; import org.omegat.gui.dialogs.NewTeamProjectController; import org.omegat.gui.dialogs.ProjectPropertiesDialog; +import org.omegat.gui.dialogs.ProjectPropertiesDialogController; import org.omegat.util.FileUtil; import org.omegat.util.FileUtil.ICollisionCallback; import org.omegat.util.HttpConnectionUtils; @@ -130,18 +132,15 @@ protected Void doInBackground() throws Exception { ProjectProperties props = new ProjectProperties(dir); props.setSourceLanguage(Preferences.getPreferenceDefault(Preferences.SOURCE_LOCALE, "AR-LB")); props.setTargetLanguage(Preferences.getPreferenceDefault(Preferences.TARGET_LOCALE, "UK-UA")); - ProjectPropertiesDialog newProjDialog = new ProjectPropertiesDialog( + final ProjectProperties newProps = ProjectPropertiesDialogController.showDialog( Core.getMainWindow().getApplicationFrame(), props, dir.getAbsolutePath(), ProjectPropertiesDialog.Mode.NEW_PROJECT); - newProjDialog.setVisible(true); - newProjDialog.dispose(); IMainWindow mainWindow = Core.getMainWindow(); Cursor hourglassCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); Cursor oldCursor = mainWindow.getCursor(); mainWindow.setCursor(hourglassCursor); - final ProjectProperties newProps = newProjDialog.getResult(); if (newProps == null) { // user clicks on 'Cancel' dir.delete(); @@ -613,12 +612,9 @@ private static void projectOpenImpl(File projectRootFolder) { while (!props.isProjectValid()) { // something wrong with the project. // We display open dialog to fix it. - ProjectPropertiesDialog prj = new ProjectPropertiesDialog( + props = ProjectPropertiesDialogController.showDialog( Core.getMainWindow().getApplicationFrame(), props, projectFile.getAbsolutePath(), ProjectPropertiesDialog.Mode.RESOLVE_DIRS); - prj.setVisible(true); - props = prj.getResult(); - prj.dispose(); if (props == null) { // user clicks on 'Cancel' return; @@ -981,13 +977,11 @@ public static void projectEditProperties() { Core.getEditor().commitAndLeave(); // displaying the dialog to change paths and other properties - ProjectPropertiesDialog prj = new ProjectPropertiesDialog(Core.getMainWindow().getApplicationFrame(), + final ProjectProperties newProps = + ProjectPropertiesDialogController.showDialog(Core.getMainWindow().getApplicationFrame(), Core.getProject().getProjectProperties(), Core.getProject().getProjectProperties().getProjectName(), ProjectPropertiesDialog.Mode.EDIT_PROJECT); - prj.setVisible(true); - final ProjectProperties newProps = prj.getResult(); - prj.dispose(); if (newProps == null) { return; } diff --git a/test/src/org/omegat/gui/dialogs/DialogsTest.java b/test/src/org/omegat/gui/dialogs/DialogsTest.java index 3ad9d3f6ef..f29fd05172 100644 --- a/test/src/org/omegat/gui/dialogs/DialogsTest.java +++ b/test/src/org/omegat/gui/dialogs/DialogsTest.java @@ -26,6 +26,7 @@ package org.omegat.gui.dialogs; import static org.junit.Assert.assertTrue; +import static org.omegat.gui.dialogs.ProjectPropertiesDialog.Mode; import java.awt.Frame; import java.awt.GraphicsEnvironment; @@ -38,7 +39,6 @@ import org.junit.Test; import org.omegat.core.TestCore; import org.omegat.core.data.ProjectProperties; -import org.omegat.gui.dialogs.ProjectPropertiesDialog.Mode; import org.omegat.util.Platform; public class DialogsTest extends TestCore { From 54abfcdd1cd9a96d6d23dd482a1e79d0e1d9870e Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Thu, 21 Mar 2024 08:15:59 +0900 Subject: [PATCH 12/12] [BUGS#1253] encode/decode URL with bracket (#989) - Add a test case with bracket and kanji characters - Change regex to match URL, that removes "\\b" in end. Signed-off-by: Hiroshi Miura --- src/org/omegat/util/HttpConnectionUtils.java | 2 +- .../org/omegat/util/HttpConnectionUtilsTest.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/org/omegat/util/HttpConnectionUtils.java b/src/org/omegat/util/HttpConnectionUtils.java index b4ae82ebf0..4afc188f1d 100644 --- a/src/org/omegat/util/HttpConnectionUtils.java +++ b/src/org/omegat/util/HttpConnectionUtils.java @@ -101,7 +101,7 @@ public final class HttpConnectionUtils { */ public static final Pattern URL_PATTERN = Pattern.compile(REGEX_URL, Pattern.CASE_INSENSITIVE); - private static final Pattern HTTP_URL_PATTERN = Pattern.compile("\\bhttps?://\\S+\\b", + private static final Pattern HTTP_URL_PATTERN = Pattern.compile("\\bhttps?://\\S+", Pattern.CASE_INSENSITIVE); /** diff --git a/test/src/org/omegat/util/HttpConnectionUtilsTest.java b/test/src/org/omegat/util/HttpConnectionUtilsTest.java index b4c80ba221..4744f942d9 100644 --- a/test/src/org/omegat/util/HttpConnectionUtilsTest.java +++ b/test/src/org/omegat/util/HttpConnectionUtilsTest.java @@ -43,6 +43,11 @@ public void testDecodeURLsInText() { String str = "1. https://fr.wikipedia.org/wiki/Science_du_syst%C3%A8me_Terre"; assertEquals("1. https://fr.wikipedia.org/wiki/Science_du_système_Terre", HttpConnectionUtils.decodeHttpURLs(str)); + String str2 = "2. https://ja.wikipedia.org/wiki/2024%E5%B9%B4%E3%81%AE%E3%82%AB%E3%82%BF%E3%83%BC%E3" + + "%83%AB%E3%82%B0%E3%83%A9%E3%83%B3%E3%83%97%E3%83%AA" + + "_%28%E3%83%AD%E3%83%BC%E3%83%89%E3%83%AC%E3%83%BC%E3%82%B9%29 参照"; + assertEquals("2. https://ja.wikipedia.org/wiki/2024年のカタールグランプリ_(ロードレース) 参照", + HttpConnectionUtils.decodeHttpURLs(str2)); } @Test @@ -65,5 +70,14 @@ public void testEncodeURLs() { HttpConnectionUtils.encodeHttpURLs(base + path)); assertEquals("https://fr.wikipedia.org/wiki/Science_du_syst%C3%A8me_Terre?query=search&lang=en", HttpConnectionUtils.encodeHttpURLs(base + path + query)); + String bracket = "https://fr.wikipedia.org/wiki/Doughnut_(modèle_économique)"; + assertEquals("https://fr.wikipedia.org/wiki/Doughnut_%28mod%C3%A8le_%C3%A9conomique%29", + HttpConnectionUtils.encodeHttpURLs(bracket)); + String multibyte = "2. https://ja.wikipedia.org/wiki/2024年のカタールグランプリ_(ロードレース)"; + assertEquals( + "2. https://ja.wikipedia.org/wiki/2024%E5%B9%B4%E3%81%AE%E3%82%AB%E3%82%BF%E3%83%BC%E3" + + "%83%AB%E3%82%B0%E3%83%A9%E3%83%B3%E3%83%97%E3%83%AA" + + "_%28%E3%83%AD%E3%83%BC%E3%83%89%E3%83%AC%E3%83%BC%E3%82%B9%29", + HttpConnectionUtils.encodeHttpURLs(multibyte)); } }