diff --git a/.github/workflows/gradle-build-master.yml b/.github/workflows/gradle-build-master.yml index 18333feb84..30ea7521a3 100644 --- a/.github/workflows/gradle-build-master.yml +++ b/.github/workflows/gradle-build-master.yml @@ -18,15 +18,11 @@ jobs: with: java-version: 17 distribution: 'temurin' - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/wrapper-validation-action@v3 name: validate gradle wrapper - name: Agree gradle-scan terms run: cat ci/gradle/gradle-scan.init.gradle >> settings.gradle - uses: gradle/actions/setup-gradle@v3 - with: - build-scan-publish: true - build-scan-terms-of-service-url: https://gradle.com/terms-of-service - build-scan-terms-of-service-agree: yes name: Setup Gradle id: setup-gradle - name: Run gradle build diff --git a/Plugins.properties b/Plugins.properties index c71810a6cc..2ba93d733c 100644 --- a/Plugins.properties +++ b/Plugins.properties @@ -52,6 +52,7 @@ plugin=org.omegat.filters3.xml.xliff.XLIFFFilter \ org.omegat.externalfinder.ExternalFinder \ org.omegat.gui.theme.DefaultFlatTheme \ org.omegat.gui.theme.DefaultClassicTheme \ + org.omegat.gui.editor.mark.WhitespaceMarker \ org.omegat.util.gui.HtmlMediaSupport \ org.omegat.core.team2.impl.FileRepository \ org.omegat.core.team2.impl.HTTPRemoteRepository \ diff --git a/build.gradle b/build.gradle index 4b1afc030c..a70b5b6e23 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ import java.nio.file.Paths import com.github.spotbugs.snom.Confidence plugins { + id 'base' id 'application' id 'java-library' id 'java-test-fixtures' @@ -401,7 +402,6 @@ def omegatJarFilename = jar.archiveFileName.get() project(":machinetranslators") {jar.enabled = false} project(":spellchecker") {jar.enabled = false} -def distsDir = file(layout.buildDirectory.file("${distsDirName}")) def assetDir = findProperty('assetDir') ?: '../' def macJRE = fileTree(dir: assetDir, include: 'OpenJDK17U-jre_x64_mac_*.tar.gz') def armMacJRE = fileTree(dir: assetDir, include: 'OpenJDK17U-jre_aarch64_mac_*.tar.gz') @@ -762,12 +762,12 @@ tasks.register('mac') { } ext.makeMacTask = { args -> - def installTaskName = args.name + "InstallDist" - def signedInstallTaskName = args.name + "InstallSignedDist" + def installTaskName = 'install' + args.name.capitalize() + "Dist" + def signedInstallTaskName = 'install' + args.name.capitalize() + "SignedDist" def distZipTaskName = args.name + "DistZip" - def signedZipTaskName = args.name + "SignedDistZip" + def signedZipTaskName = args.name + "Signed" def notarizeTaskName = args.name + "Notarize" - def stapledNotarizedDistZipTaskName = args.name + "StapledNotarizedDistZip" + def stapledNotarizedDistZipTaskName = args.name + "StapledNotarized" tasks.register(distZipTaskName, Zip) { description = "Create Mac distribution for ${args.name}" @@ -803,6 +803,12 @@ ext.makeMacTask = { args -> } } } + outputs.upToDateWhen { + // detect up-to-date when OmegaT.jar exists and newer than libs/OmegaT.jar + def f1 = base.distsDirectory.file(archiveFileName).get().asFile + def f2 = base.libsDirectory.file('OmegaT.jar').get().asFile + f1.exists() && f2.exists() && f1.lastModified() > f2.lastModified() + } onlyIf { condition(!args.jrePath || !args.jrePath.empty, 'JRE not found') } @@ -845,7 +851,13 @@ ext.makeMacTask = { args -> } } duplicatesStrategy = DuplicatesStrategy.INCLUDE - destinationDir file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}")) + destinationDir file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}")) + outputs.upToDateWhen { + // detect up-to-date when OmegaT.jar exists and newer than libs/OmegaT.jar + def f1 = file("$destinationDir/OmegaT.app/Contents/Java/OmegaT.jar") + def f2 = base.libsDirectory.file('OmegaT.jar').get().asFile + f1.exists() && f2.exists() && f1.lastModified() > f2.lastModified() + } doFirst { delete "$destinationDir/OmegaT.app/Contents/PlugIns/jre.bundle" } @@ -1167,8 +1179,8 @@ tasks.register("linuxRpmDist", Exec) { // We bundle our startup scripts separately, so disable startScripts. startScripts.enabled = false -installDist { -} +// installDist insists on destination executable directory even when disable start script. +application.executableDir = "" // Read in all our custom messages and massage them for inclusion in the .iss ext.getInnoSetupCustomMessages = { @@ -1198,18 +1210,69 @@ tasks.register('win') { ext.makeWinTask = { args -> def fullVersion = project.version + omtVersion.beta def installerBasename = "OmegaT_${fullVersion}_${args.suffix}" - def installerExe = layout.buildDirectory.file("${distsDirName}/${installerBasename}.exe") - def signedExe = layout.buildDirectory.file("${distsDirName}/${installerBasename}_Signed.exe") - + def installerWinExe = base.distsDirectory.file("${installerBasename}.exe") + def signedWinExe = base.distsDirectory.file("${installerBasename}_Signed.exe") + def prepDistsTaskName = "${args.name}Prep" + def genDistsTaskName = "${args.name}Gen" + def distsTaskName = "${args.name}" def signedTaskName = "${args.name}Signed" + def signedTaskCommandArgs = { arg2 -> + def exe = exePresent('osslsigncode') ? 'osslsigncode' : file('ci/osslsigncode').toString() + def commandArgs = [exe, 'sign'] + if (project.hasProperty('pkcs11module')) { + commandArgs.addAll('-pkcs11module', project.property('pkcs11module')) + } + if (project.hasProperty('winCodesignCert')) { + commandArgs.addAll('-certs', project.property('winCodesignCert')) + } + if (project.hasProperty('pkcs11cert')) { + commandArgs.addAll('-pkcs11cert', project.property('pkcs11cert')) + } + if (project.hasProperty('winCodesignPassword')) { + commandArgs.addAll('-pass', project.property('winCodesignPassword')) + } + if (project.hasProperty('winCodesignDevice')) { + envVars['USBDEV'] = project.property('winCodesignDevice') + } + commandArgs.addAll( + '-t', project.hasProperty('winCodesignTimestampUrl') ? project.property('winCodesignTimestampUrl') : + 'http://time.certum.pl/', + '-n', application.applicationName, '-i', omtWebsite, '-h', 'sha256', + '-in', installerWinExe.get().asFile, + '-out', signedWinExe.get().asFile + ) + return commandArgs + } - tasks.register(args.name, Sync) { - description = "Create a Windows installer for ${args.name} distro. " + - 'Requires Inno Setup (http://www.jrsoftware.org/isinfo.php).' + tasks.register(prepDistsTaskName, Sync) { + onlyIf { + conditions([!args.jrePath || !args.jrePath.empty, 'JRE not found'], + [exePresent('iscc') || exePresent('docker') || exePresent('nerdctl'), + 'InnoSetup or Docker not installed']) + } + doFirst { + delete "$destinationDir/jre" + delete installerWinExe + } with distributions.main.contents - dependsOn createAllExecutables + destinationDir file(layout.buildDirectory.file("innosetup/${args.name}")) + outputs.upToDateWhen { + // detect up-to-date when OmegaT.jar exists and newer than libs/OmegaT.jar + def f1 = layout.buildDirectory.file("innosetup/${args.name}/OmegaT.jar").get().asFile + def f2 = base.libsDirectory.file('OmegaT.jar').get().asFile + f1.exists() && f2.exists() && f1.lastModified() > f2.lastModified() + } from('release/win32-specific') { include 'OmegaT.l4J.ini' + include 'OmegaT.iss' + filter(ReplaceTokens, tokens: [ + VERSION_NUMBER_SUBST : fullVersion, + OUTPUT_BASENAME_SUBST: installerBasename.toString(), + CUSTOM_MESSAGES_SUBST: getInnoSetupCustomMessages(), + ARCHITECTURE_SUBST : args.arch ?: '' + ]) + filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) + filteringCharset = 'UTF-8' } from('build/launch4j') { include '*.exe' @@ -1222,56 +1285,47 @@ ext.makeWinTask = { args -> } } } - destinationDir file(layout.buildDirectory.file("innosetup/${args.name}")) - outputs.file installerExe + dependsOn createAllExecutables + } + + tasks.register(genDistsTaskName, Exec) { onlyIf { conditions([!args.jrePath || !args.jrePath.empty, 'JRE not found'], [exePresent('iscc') || exePresent('docker') || exePresent('nerdctl'), 'InnoSetup or Docker not installed']) } - doFirst { - delete "$destinationDir/jre" - delete installerExe - project.copy { - from 'build' - into 'build' - include "innosetup/${args.name}/**/*" - dirMode 0777 - fileMode 0666 - } + dependsOn prepDistsTaskName + outputs.upToDateWhen { + // detect up-to-date when OmegaT.jar exists and newer than libs/OmegaT.jar + def f1 = layout.buildDirectory.file("innosetup/${args.name}/${installerBasename}.exe").get().asFile + def f2 = layout.buildDirectory.file("innosetup/${args.name}/OmegaT.jar").get().asFile + f1.exists() && f2.exists() && f1.lastModified() > f2.lastModified() } - doLast { - project.copy { - from('release/win32-specific') { - include 'OmegaT.iss' - } - into(destinationDir) - filter(ReplaceTokens, tokens: [ - VERSION_NUMBER_SUBST : fullVersion, - OUTPUT_BASENAME_SUBST: installerBasename.toString(), - CUSTOM_MESSAGES_SUBST: getInnoSetupCustomMessages(), - ARCHITECTURE_SUBST : args.arch ?: '' - ]) - filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) - filteringCharset = 'UTF-8' - } - exec { - // You'd think we could just set the PATH, but there be dragons here - // https://github.com/palantir/gradle-docker/issues/162 - def exe = exePresent('iscc') ? 'iscc' : file('ci/iscc') - commandLine exe, "${destinationDir}/OmegaT.iss" - } - project.copy { - from("${destinationDir}/${installerBasename}.exe") - into distsDir - } - delete file("${destinationDir}/${installerBasename}.exe") + // You'd think we could just set the PATH, but there be dragons here + // https://github.com/palantir/gradle-docker/issues/162 + def exe = exePresent('iscc') ? 'iscc' : file('ci/iscc') + def iss = layout.buildDirectory.file("innosetup/${args.name}/OmegaT.iss").get().asFile + commandLine exe, '/Qp', iss + } + + tasks.register(distsTaskName, Copy) { + description = "Create a Windows installer for ${args.name} distro. " + + 'Requires Inno Setup (http://www.jrsoftware.org/isinfo.php).' + onlyIf { + conditions([!args.jrePath || !args.jrePath.empty, 'JRE not found'], + [exePresent('iscc') || exePresent('docker') || exePresent('nerdctl'), + 'InnoSetup or Docker not installed']) } + from layout.buildDirectory.file("innosetup/${args.name}/${installerBasename}.exe") + into base.distsDirectory + outputs.file installerWinExe + dependsOn genDistsTaskName } + tasks.register(signedTaskName, Exec) { group = 'omegat distribution' - inputs.file installerExe - outputs.file signedExe + inputs.file installerWinExe skipWhenEmpty() + outputs.file signedWinExe // Starting from Nov 2022, certification provider force to use HSM to // store private keys. Starting on June 1, 2023, at 00:00 UTC, industry // standards will require private keys for standard code signing @@ -1289,34 +1343,13 @@ ext.makeWinTask = { args -> // [exePresent('osslsigncode') || exePresent('docker') || exePresent('nerdctl'), // 'neither osslsigncode or docker/nerdctl is not installed']) } - def exe = exePresent('osslsigncode') ? 'osslsigncode' : file('ci/osslsigncode') - def envVars = [:] - def commandArgs = [exe, 'sign'] - if (project.hasProperty('pkcs11module')) { - commandArgs.addAll('-pkcs11module', project.property('pkcs11module')) - } - if (project.hasProperty('winCodesignCert')) { - commandArgs.addAll('-certs', project.property('winCodesignCert')) - } - if (project.hasProperty('pkcs11cert')) { - commandArgs.addAll('-pkcs11cert', project.property('pkcs11cert')) - } - if (project.hasProperty('winCodesignPassword')) { - commandArgs.addAll('-pass', project.property('winCodesignPassword')) - } - if (project.hasProperty('winCodesignDevice')) { - envVars['USBDEV'] = project.property('winCodesignDevice') + doFirst { + delete signedWinExe } - commandArgs.addAll( - '-t', project.hasProperty('winCodesignTimestampUrl') ? project.property('winCodesignTimestampUrl') : - 'http://time.certum.pl/', - '-n', application.applicationName, '-i', omtWebsite, '-h', 'sha256', - '-in', installerExe.get().asFile.toString(), - '-out', signedExe.get().asFile.toString() - ) - commandLine(commandArgs) + def envVars = [:] + commandLine(signedTaskCommandArgs()) environment(envVars) - dependsOn args.name + dependsOn distsTaskName } assemble.dependsOn args.name, signedTaskName win.dependsOn args.name, signedTaskName @@ -1354,18 +1387,17 @@ processResources { } tasks.register('checksums') { - def algos = ['SHA-512', 'MD5'] + def algos = ['SHA-512'] description = "Generate ${algos.join(', ')} checksums for distribution files" - inputs.files fileTree(dir: distsDir, exclude: 'checksums') - def checksumsDir = file("${distsDir}/checksums") - outputs.dir checksumsDir + inputs.files fileTree(dir: base.distsDirectory, exclude: 'checksums') + outputs.dir base.distsDirectory.dir('checksums') onlyIf { - condition(distsDir.directory, 'Distfiles not found') + condition(base.distsDirectory.get().asFile.directory, 'Distfiles not found') } doLast { - distsDir.listFiles().findAll { it.file }.each { f -> + base.distsDirectory.get().asFile.listFiles().findAll { it.file }.each { f -> algos.each { algo -> - ant.checksum file: f, algorithm: algo, todir: checksumsDir + ant.checksum file: f, algorithm: algo, todir: distsDirectory.dir('checksums').get().asFile } } } diff --git a/ci/azure-pipelines/build_steps.yml b/ci/azure-pipelines/build_steps.yml index 63494fc753..952419a582 100644 --- a/ci/azure-pipelines/build_steps.yml +++ b/ci/azure-pipelines/build_steps.yml @@ -31,7 +31,7 @@ steps: displayName: 'Preparation for build' - task: Gradle@3 inputs: - tasks: 'clean sourceDistZip distZip mac linux win' + tasks: 'clean sourceDistZip distZip mac linux win checksums' options: '--build-cache -PenvIsCi -PassetDir=$(System.ArtifactsDirectory)/asset' jdkVersionOption: '1.17' displayName: 'Build distribution packages and docs' diff --git a/ci/azure-pipelines/publish_release.yml b/ci/azure-pipelines/publish_release.yml index cb0a75e854..57891d1d1e 100644 --- a/ci/azure-pipelines/publish_release.yml +++ b/ci/azure-pipelines/publish_release.yml @@ -15,5 +15,5 @@ steps: dest=$(SOURCEFORGE_FILE_USER)@frs.sourceforge.net destdir="/home/frs/project/omegat/OmegaT\\ -\\ Latest/OmegaT\\ ${{ parameters.omegatVersion }}/" echo "mkdir $destdir" | SSHPASS=$(SOURCEFORGE_FILE_PASS) sshpass -e sftp -v $dest || true - SSHPASS=$(SOURCEFORGE_FILE_PASS) sshpass -e scp -v -oStrictHostKeyChecking=no $srcdir/* "$dest:$destdir" + SSHPASS=$(SOURCEFORGE_FILE_PASS) sshpass -e scp -r -v -oStrictHostKeyChecking=no $srcdir/* "$dest:$destdir" diff --git a/ci/azure-pipelines/publish_weekly.yml b/ci/azure-pipelines/publish_weekly.yml index f56c668cf6..a12368fccc 100644 --- a/ci/azure-pipelines/publish_weekly.yml +++ b/ci/azure-pipelines/publish_weekly.yml @@ -16,4 +16,4 @@ steps: dest=$(SOURCEFORGE_FILE_USER)@frs.sourceforge.net destdir=/home/frs/project/omegat/Weekly echo "mkdir $destdir" | SSHPASS=$(SOURCEFORGE_FILE_PASS) sshpass -e sftp -v $dest || true - SSHPASS=$(SOURCEFORGE_FILE_PASS) sshpass -e scp -v -oStrictHostKeyChecking=no $srcdir/* $dest:$destdir + SSHPASS=$(SOURCEFORGE_FILE_PASS) sshpass -e scp -r -v -oStrictHostKeyChecking=no $srcdir/* $dest:$destdir diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c8ef652e84..c995c787c8 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.20" +groovy = "4.0.21" junit = "4.13.2" commons_lang3 = "3.14.0" commons_io = "2.15.1" @@ -39,16 +39,16 @@ pdfbox = "3.0.2" maligna = "3.0.1" trie4j = "0.9.10_1" dsl4j = "0.7.1" -stardict4j = "0.6.1" +stardict4j = "0.6.3" juniversalchardet = "2.4.0" -mnemonics = "1.2-SNAPSHOT" +mnemonics = "1.2" jmyspell = "1.0.0-beta-2" hunspell = "2.1.2" xjc = "2.3.4" jna = "5.13.0" jfa = "1.2.0" tipoftheday = "0.4.4" -flatlaf="3.4" +flatlaf="3.4.1" [libraries] slf4j-api = {group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j"} @@ -146,7 +146,7 @@ jgit = ["jgit", "jgit-agent", "jgit-http", "jgit-ssh"] dictionary = ["trie4j", "dsl4j", "stardict4j", "jsoup"] [plugins] -spotbugs = {id = "com.github.spotbugs", version = "6.0.8"} +spotbugs = {id = "com.github.spotbugs", version = "6.0.13"} spotless = {id = "com.diffplug.spotless", version = "6.25.0"} launch4j = {id = "edu.sc.seis.launch4j", version = "3.0.5"} versions = {id = "com.github.ben-manes.versions", version = "0.51.0"} diff --git a/release/changes.txt b/release/changes.txt index 634145d654..612e20afdb 100644 --- a/release/changes.txt +++ b/release/changes.txt @@ -2,7 +2,7 @@ OmegaT 6.1.0 ---------------------------------------------------------------------- 44 Enhancement - 68 Bug fixes + 69 Bug fixes 5 Localisation updates ---------------------------------------------------------------------- 6.1.0 vs 6.0.0 @@ -136,6 +136,9 @@ Bug fixes: + - OmegaT does not show entreis of stardict-JMDict dictionary + https://sourceforge.net/p/omegat/bugs/1255/ + - Short glossary items are not recognized with Korean source https://sourceforge.net/p/omegat/bugs/1250/ diff --git a/settings.gradle b/settings.gradle index a228f036e9..98d0f930bc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,13 @@ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' - id 'com.gradle.enterprise' version '3.16.2' + id 'com.gradle.develocity' version '3.17.2' +} +develocity { + buildScan { + publishing.onlyIf { "true".equals(System.getProperty("envIsCi")) } + termsOfUseUrl = "https://gradle.com/terms-of-service" + termsOfUseAgree = "yes" + } } rootProject.name = 'OmegaT' include("machinetranslators:apertium", diff --git a/src/org/omegat/core/dictionaries/StarDict.java b/src/org/omegat/core/dictionaries/StarDict.java index 7a181a7721..51ec114ace 100644 --- a/src/org/omegat/core/dictionaries/StarDict.java +++ b/src/org/omegat/core/dictionaries/StarDict.java @@ -181,7 +181,11 @@ private static DictionaryEntry convertEntry(StarDictDictionary.Entry entry) { } else if (entry.getType().equals(StarDictDictionary.EntryType.PHONETIC)) { sb.append("(").append(entry.getArticle()).append(")"); } else if (entry.getType().equals(StarDictDictionary.EntryType.HTML)) { - sb.append(entry.getArticle()); + Document document = Jsoup.parse(entry.getArticle()); + Safelist safelist = Safelist.relaxed(); + Cleaner cleaner = new Cleaner(safelist); + document = cleaner.clean(document); + sb.append(document.body().html()); } else if (entry.getType().equals(StarDictDictionary.EntryType.PANGO)) { Document document = Jsoup.parse(entry.getArticle()); Safelist safelist = new Safelist() diff --git a/src/org/omegat/core/team2/gui/UserPassDialog.java b/src/org/omegat/core/team2/gui/UserPassDialog.java index 488d96f322..09a809534d 100644 --- a/src/org/omegat/core/team2/gui/UserPassDialog.java +++ b/src/org/omegat/core/team2/gui/UserPassDialog.java @@ -46,6 +46,7 @@ import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; @@ -82,50 +83,50 @@ public UserPassDialog(java.awt.Frame parent) { usernameField.requestFocusInWindow(); } - private void setActions() { - usernameField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - checkCredentials(); - } + abstract static class CheckDocumentAction implements DocumentListener { - @Override - public void removeUpdate(DocumentEvent e) { - checkCredentials(); - } + @Override + public void insertUpdate(final DocumentEvent e) { + update(); + } - @Override - public void changedUpdate(DocumentEvent e) { - checkCredentials(); - } - }); - passwordField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - checkCredentials(); - } + @Override + public void removeUpdate(final DocumentEvent e) { + update(); + } + + @Override + public void changedUpdate(final DocumentEvent e) { + update(); + } + + abstract void update(); + } + private void setActions() { + usernameField.getDocument().addDocumentListener(new CheckDocumentAction() { @Override - public void removeUpdate(DocumentEvent e) { - checkCredentials(); + protected void update() { + SwingUtilities.invokeLater(() -> checkCredentials()); } - + }); + passwordField.getDocument().addDocumentListener(new CheckDocumentAction() { @Override - public void changedUpdate(DocumentEvent e) { - checkCredentials(); + protected void update() { + SwingUtilities.invokeLater(() -> checkCredentials()); } }); toggleButton.addActionListener(e -> { // Toggle between showing/hiding the password if (toggleButton.isSelected()) { - passwordField.setEchoChar((char) 0); // Show password in plaintext - toggleButton.setIcon(new ImageIcon(Objects.requireNonNull(getClass().getResource( - "/org/omegat/gui/resources/eye.png")))); // NOI18N + passwordField.setEchoChar((char) 0); + toggleButton.setIcon(new ImageIcon( + Objects.requireNonNull(getClass().getResource("/org/omegat/gui/resources/eye.png")))); // NOI18N } else { passwordField.setEchoChar('●'); - toggleButton.setIcon(new ImageIcon(Objects.requireNonNull(getClass().getResource( - "/org/omegat/gui/resources/eye-slash.png")))); // NOI18N + toggleButton.setIcon(new ImageIcon(Objects + .requireNonNull(getClass().getResource("/org/omegat/gui/resources/eye-slash.png")))); // NOI18N } }); } @@ -133,20 +134,38 @@ public void changedUpdate(DocumentEvent e) { private void checkCredentials() { String username = usernameField.getText(); char[] password = passwordField.getPassword(); - boolean isValid = !username.trim().isEmpty() && password.length > 0; + boolean isUsernameValid = !username.startsWith(" ") && !username.endsWith(" "); + if (!isUsernameValid) { + usernamePanel.setBackground(Color.YELLOW); + messageArea.setText(OStrings.getString("TEAM_USERPASS_EXTRA_SPACE")); + okButton.setEnabled(false); + return; + } else { + usernamePanel.setBackground(mainPanel.getBackground()); + } - if (isValid) { - isValid = username.trim().length() == username.length() && password[0] != ' ' - && password[password.length - 1] != ' '; - if (!isValid) { - setMessage(OStrings.getString("TEAM_USERPASS_EXTRA_SPACE")); - } + // check if the username or password is empty + if (username.trim().isEmpty() || password.length == 0) { + passwordPanel.setBackground(mainPanel.getBackground()); + messageArea.setText(""); + okButton.setEnabled(false); + return; } - if (isValid) { - resetMessage(); + + // check if the password has a preceding or trailing space character(s) + boolean isPasswordValid = password[0] != ' ' && password[password.length - 1] != ' '; + if (!isPasswordValid) { + passwordPanel.setBackground(Color.YELLOW); + messageArea.setText(OStrings.getString("TEAM_USERPASS_EXTRA_SPACE")); + okButton.setEnabled(false); + return; + } else { + passwordPanel.setBackground(mainPanel.getBackground()); } - okButton.setEnabled(isValid); + // everything is ok. + messageArea.setText(""); + okButton.setEnabled(true); } /** @@ -169,15 +188,19 @@ private void initComponents() { descriptionTextArea = new JTextArea(); JLabel usernameLabel = new JLabel(); usernameField = new JTextField(); + usernameField.setPreferredSize(new Dimension(352, 30)); + usernamePanel = new JPanel(); JLabel passwordLabel = new JLabel(); passwordField = new JPasswordField(); + passwordField.setPreferredSize(new Dimension(320, 30)); + passwordPanel = new JPanel(); toggleButton = new JToggleButton(); perHostCheckBox = new JCheckBox(); messageArea = new JTextArea(); okButton = new JButton(); JButton cancelButton = new JButton(); - Icon eyeSlash = new ImageIcon(Objects.requireNonNull(getClass().getResource( - "/org/omegat/gui/resources/eye-slash.png"))); + Icon eyeSlash = new ImageIcon( + Objects.requireNonNull(getClass().getResource("/org/omegat/gui/resources/eye-slash.png"))); toggleButton.setIcon(eyeSlash); toggleButton.setDisabledIcon(eyeSlash); toggleButton.setMargin(new java.awt.Insets(1, 1, 1, 1)); @@ -225,7 +248,8 @@ public void windowClosing(WindowEvent evt) { gridBagConstraints.anchor = GridBagConstraints.WEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - mainPanel.add(usernameField, gridBagConstraints); + usernamePanel.add(usernameField); + mainPanel.add(usernamePanel, gridBagConstraints); passwordLabel.setLabelFor(passwordField); org.openide.awt.Mnemonics.setLocalizedText(passwordLabel, OStrings.getString("LOGIN_PASSWORD")); // NOI18N @@ -245,12 +269,9 @@ public void windowClosing(WindowEvent evt) { gridBagConstraints.anchor = GridBagConstraints.WEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - mainPanel.add(passwordField, gridBagConstraints); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 6; - gridBagConstraints.anchor = GridBagConstraints.WEST; - mainPanel.add(toggleButton, gridBagConstraints); + passwordPanel.add(passwordField); + passwordPanel.add(toggleButton); + mainPanel.add(passwordPanel, gridBagConstraints); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; @@ -313,7 +334,9 @@ private void doClose(int retStatus) { private JButton okButton; private JTextArea descriptionTextArea; private JTextField usernameField; + private JPanel usernamePanel; private JPasswordField passwordField; + private JPanel passwordPanel; private JCheckBox perHostCheckBox; private JTextArea messageArea; private JToggleButton toggleButton; @@ -348,14 +371,4 @@ public void setDescription(String description) { public void setPerHostCheckBoxText(String text) { org.openide.awt.Mnemonics.setLocalizedText(perHostCheckBox, text); } - - public void setMessage(String text) { - messageArea.setText(text); - messageArea.setBackground(Color.red); - } - - public void resetMessage() { - messageArea.setText(""); - messageArea.setBackground(mainPanel.getBackground()); - } }