From 59ed1989942ef16c530322102b45d65bdf46d79c Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sat, 1 Nov 2025 14:25:32 +0100
Subject: [PATCH 01/44] Update the scan calls to send
maxHashExtractionFileSize, for now hardcoded
---
.../pwss/model/request/scan/StartScanAllRequest.java | 9 +++++++++
.../model/request/scan/StartSingleScanRequest.java | 3 ++-
src/main/java/org/pwss/service/ScanService.java | 12 +++++++++---
src/main/java/org/pwss/service/network/Endpoint.java | 2 +-
4 files changed, 21 insertions(+), 5 deletions(-)
create mode 100644 src/main/java/org/pwss/model/request/scan/StartScanAllRequest.java
diff --git a/src/main/java/org/pwss/model/request/scan/StartScanAllRequest.java b/src/main/java/org/pwss/model/request/scan/StartScanAllRequest.java
new file mode 100644
index 0000000..c12e6c0
--- /dev/null
+++ b/src/main/java/org/pwss/model/request/scan/StartScanAllRequest.java
@@ -0,0 +1,9 @@
+package org.pwss.model.request.scan;
+
+/**
+ * Represents a request to start a scan for all files.
+ *
+ * @param maxHashExtractionFileSize The maximum file size (in bytes) for which hash extraction should be performed.
+ */
+public record StartScanAllRequest(long maxHashExtractionFileSize) {
+}
diff --git a/src/main/java/org/pwss/model/request/scan/StartSingleScanRequest.java b/src/main/java/org/pwss/model/request/scan/StartSingleScanRequest.java
index 00c3a53..c6c01dd 100644
--- a/src/main/java/org/pwss/model/request/scan/StartSingleScanRequest.java
+++ b/src/main/java/org/pwss/model/request/scan/StartSingleScanRequest.java
@@ -4,6 +4,7 @@
* Request to start a single scan by a monitored directory's ID.
*
* @param id The ID of the monitored directory to scan.
+ * @param maxHashExtractionFileSize The maximum file size (in bytes) for which hash extraction should be performed.
*/
-public record StartSingleScanRequest(long id) {
+public record StartSingleScanRequest(long id, long maxHashExtractionFileSize) {
}
diff --git a/src/main/java/org/pwss/service/ScanService.java b/src/main/java/org/pwss/service/ScanService.java
index c932563..a9eb38d 100644
--- a/src/main/java/org/pwss/service/ScanService.java
+++ b/src/main/java/org/pwss/service/ScanService.java
@@ -18,6 +18,7 @@
import org.pwss.model.entity.Scan;
import org.pwss.model.request.scan.GetMostRecentScansRequest;
import org.pwss.model.request.scan.GetScanDiffsRequest;
+import org.pwss.model.request.scan.StartScanAllRequest;
import org.pwss.model.request.scan.StartSingleScanRequest;
import org.pwss.model.response.LiveFeedResponse;
import org.pwss.service.network.Endpoint;
@@ -44,8 +45,11 @@ public ScanService() {
* @throws ExecutionException If an error occurs during the asynchronous execution of the request.
* @throws InterruptedException If the thread executing the request is interrupted.
*/
- public boolean startScan() throws StartFullScanException, ExecutionException, InterruptedException {
- HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN, null);
+ public boolean startScan() throws StartFullScanException, ExecutionException, InterruptedException, JsonProcessingException {
+ // Fake properties read
+ long maxHashExtractionFileSize = 1024 * 1024 * 2047; // 2GB - 1MB
+ String body = objectMapper.writeValueAsString(new StartScanAllRequest(maxHashExtractionFileSize));
+ HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN, body);
return switch (response.statusCode()) {
case 200 -> true;
@@ -71,7 +75,9 @@ public boolean startScan() throws StartFullScanException, ExecutionException, In
* @throws JsonProcessingException If an error occurs while serializing the start scan request to JSON.
*/
public boolean startScanById(long id) throws StartScanByIdException, ExecutionException, InterruptedException, JsonProcessingException {
- String body = objectMapper.writeValueAsString(new StartSingleScanRequest(id));
+ // Fake properties read
+ long maxHashExtractionFileSize = 1024 * 1024 * 2047; // 2GB - 1MB
+ String body = objectMapper.writeValueAsString(new StartSingleScanRequest(id, maxHashExtractionFileSize));
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN_ID, body);
return switch (response.statusCode()) {
diff --git a/src/main/java/org/pwss/service/network/Endpoint.java b/src/main/java/org/pwss/service/network/Endpoint.java
index c6b6ef3..a02c421 100644
--- a/src/main/java/org/pwss/service/network/Endpoint.java
+++ b/src/main/java/org/pwss/service/network/Endpoint.java
@@ -25,7 +25,7 @@ public enum Endpoint {
/**
* Endpoint for starting a file integrity scan of all directories.
*/
- START_SCAN(HTTP_Method.GET, A.BASE_URL + A.SCAN + "start/all"),
+ START_SCAN(HTTP_Method.POST, A.BASE_URL + A.SCAN + "start/all"),
/**
* Endpoint for starting a file integrity scan of a specific directory.
*/
From db3d9fb4ddf267d0ebfd5b03cdf436a764748d8a Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sat, 1 Nov 2025 15:17:10 +0100
Subject: [PATCH 02/44] Added max hash extraction file size config value
---
.../java/org/pwss/app_settings/AppConfig.java | 19 +++++
.../org/pwss/app_settings/ConfigLoader.java | 77 ++++++++++++++++++-
2 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/app_settings/AppConfig.java b/src/main/java/org/pwss/app_settings/AppConfig.java
index c967c4d..22f2e26 100644
--- a/src/main/java/org/pwss/app_settings/AppConfig.java
+++ b/src/main/java/org/pwss/app_settings/AppConfig.java
@@ -27,6 +27,12 @@ public final class AppConfig {
* when the class is initialized.
*/
public final static String LICENSE_KEY;
+ /**
+ * Maximum file size (in bytes) for hash extraction. This value is loaded from
+ * the configuration file
+ * when the class is initialized.
+ */
+ public final static long MAX_HASH_EXTRACTION_FILE_SIZE;
/**
* ConfigLoader instance used to load and manage configuration values.
@@ -41,6 +47,7 @@ public final class AppConfig {
USE_SPLASH_SCREEN = configLoader.isUseSplashScreen();
APP_THEME = configLoader.getAppTheme();
LICENSE_KEY = configLoader.getLicenseKey();
+ MAX_HASH_EXTRACTION_FILE_SIZE = configLoader.getHashExtractionMaxFileSizeValue();
}
/**
@@ -80,4 +87,16 @@ public static final boolean setLicenseKey(String licenseKey) {
return configLoader.setLicenseKey(licenseKey);
}
+ /**
+ * Sets the maximum file size for hash extraction in the configuration file.
+ * This change will take effect only after
+ * the frontend application is restarted.
+ *
+ * @param maxFileSize The value to set for the maximum file size (in bytes)
+ * @return true if setting was successful, otherwise false
+ */
+ public static final boolean setMaxHashExtractionFileSize(long maxFileSize) {
+ return configLoader.setMaxHashExtractionFileSize(String.valueOf(maxFileSize));
+ }
+
}
diff --git a/src/main/java/org/pwss/app_settings/ConfigLoader.java b/src/main/java/org/pwss/app_settings/ConfigLoader.java
index 5ced67d..f197389 100644
--- a/src/main/java/org/pwss/app_settings/ConfigLoader.java
+++ b/src/main/java/org/pwss/app_settings/ConfigLoader.java
@@ -29,6 +29,10 @@ final class ConfigLoader {
* Key in the properties file for license key setting.
*/
private final String LICENSE_KEY = "frontend.licensekey";
+ /**
+ * Key in the properties file for maximum hash extraction file size setting.
+ */
+ private final String MAX_HASH_EXTRACTION_FILE_SIZE_KEY = "scanner.max_hash_extraction_file_size";
/**
* Path to the configuration file.
@@ -52,6 +56,10 @@ final class ConfigLoader {
* License key for the application.
*/
private final String licenseKey;
+ /**
+ * Maximum hash extraction file size.
+ */
+ private final long maxHashExtractionFileSize;
/**
* Constructor that loads configuration settings from the properties file and
@@ -67,10 +75,12 @@ final class ConfigLoader {
this.useSplashScreen = true;
this.appTheme = 1;
this.licenseKey = "none";
+ this.maxHashExtractionFileSize = -1;
} else {
this.useSplashScreen = getSplashScreenFlagFromConfigString(getSplashScreenProperty());
this.appTheme = getAppThemeValueFromConfigString(getAppThemeProperty());
this.licenseKey = getLicenseKeyProperty();
+ this.maxHashExtractionFileSize = getMaxHashExtractionFileSizeFromConfigString(getMaxHashExtractionFileSizeProperty());
}
}
@@ -79,7 +89,7 @@ final class ConfigLoader {
* Parses the splash screen flag from a configuration string.
*
* @param configFileString The configuration string to be parsed
- * @return falsee if the string is "false" (case insensitive), otherwise true
+ * @return false if the string is "false" (case insensitive), otherwise true
*/
private final boolean getSplashScreenFlagFromConfigString(String configFileString) {
@@ -124,6 +134,29 @@ private final int getAppThemeValueFromConfigString(String configFileString) {
}
+ /**
+ * Parses the maximum hash extraction file size from a configuration string.
+ *
+ * @return The long value of the maximum hash extraction file size, or -1 if
+ * parsing fails or the value is invalid
+ */
+ private final long getMaxHashExtractionFileSizeFromConfigString(String configFileString) {
+ try {
+ long maxHashExtractionFileSizeValue = Long.parseLong(configFileString);
+
+ if (maxHashExtractionFileSizeValue >= -1)
+ return maxHashExtractionFileSizeValue;
+ else
+ return -1;
+ }
+
+ catch (Exception exception) {
+ log.debug("Could not parse max hash extraction file size value from app settings", exception);
+ log.error("Could not parse max hash extraction file size value from app settings {}", exception.getMessage());
+ return -1;
+ }
+ }
+
/**
* Loads the configuration properties from the file specified by
* CONFIG_FILE_PATH.
@@ -200,6 +233,23 @@ private final String getLicenseKeyProperty() {
}
+ /**
+ * Retrieves the maximum hash extraction file size property value from the
+ * properties file.
+ *
+ * @return The maximum hash extraction file size property value, or "-1" if an
+ * error occurs
+ */
+ private final String getMaxHashExtractionFileSizeProperty() {
+ try {
+ return properties.getProperty(MAX_HASH_EXTRACTION_FILE_SIZE_KEY);
+ } catch (Exception exception) {
+ log.debug("Max hash extraction file size property could not be fetched", exception);
+ log.error("Max hash extraction file size property could not be fetched {}", exception.getMessage());
+ return "-1";
+ }
+ }
+
/**
* Sets the splash screen flag in the properties file.
*
@@ -260,6 +310,27 @@ final boolean setLicenseKey(String licenseKey) {
}
}
+ /**
+ * Sets the maximum hash extraction file size value in the properties file.
+ *
+ * @param maxHashExtractionFileSize The value to set for the maximum hash
+ * extraction file size
+ * @return true if setting was successful, otherwise false
+ */
+ final boolean setMaxHashExtractionFileSize(String maxHashExtractionFileSize) {
+
+ properties.setProperty(MAX_HASH_EXTRACTION_FILE_SIZE_KEY, maxHashExtractionFileSize);
+
+ try (FileOutputStream fos = new FileOutputStream(CONFIG_FILE_PATH)) {
+ properties.store(fos, null);
+ return true;
+ } catch (Exception exception) {
+ log.debug("Max hash extraction file size could not be set in app.config file", exception);
+ log.error("Max hash extraction file size could not be set in app.config file", exception.getMessage());
+ return false;
+ }
+ }
+
/**
* Gets the value indicating whether to use splash screen or not.
*
@@ -286,4 +357,8 @@ final int getAppTheme() {
final String getLicenseKey() {
return licenseKey;
}
+
+ final long getHashExtractionMaxFileSizeValue() {
+ return maxHashExtractionFileSize;
+ }
}
\ No newline at end of file
From 4fb1bf42c4f8b9a46961c1b6f2fd763dc2e29cd3 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sat, 1 Nov 2025 15:21:08 +0100
Subject: [PATCH 03/44] Send actual config value in the requests
---
app_storage/metadata/metadata.temp | 2 +-
options/app.config | 3 ++-
src/main/java/org/pwss/service/ScanService.java | 11 +++++++----
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/app_storage/metadata/metadata.temp b/app_storage/metadata/metadata.temp
index ca44c66..124a382 100644
--- a/app_storage/metadata/metadata.temp
+++ b/app_storage/metadata/metadata.temp
@@ -1 +1 @@
-#Wed Oct 15 22:29:09 CEST 2025
+#Thu Oct 30 19:28:32 CET 2025
diff --git a/options/app.config b/options/app.config
index 8ba896f..61aeeb3 100644
--- a/options/app.config
+++ b/options/app.config
@@ -1,4 +1,5 @@
-#Sat Oct 18 15:00:03 CEST 2025
+#Sat Nov 01 14:24:19 CET 2025
frontend.licensekey=
frontend.splashscreen=false
frontend.theme=1
+scanner.max_hash_extraction_file_size=-1
\ No newline at end of file
diff --git a/src/main/java/org/pwss/service/ScanService.java b/src/main/java/org/pwss/service/ScanService.java
index a9eb38d..c297f41 100644
--- a/src/main/java/org/pwss/service/ScanService.java
+++ b/src/main/java/org/pwss/service/ScanService.java
@@ -6,6 +6,7 @@
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import org.pwss.app_settings.AppConfig;
import org.pwss.exception.scan.GetAllMostRecentScansException;
import org.pwss.exception.scan.GetMostRecentScansException;
import org.pwss.exception.scan.GetScanDiffsException;
@@ -46,8 +47,9 @@ public ScanService() {
* @throws InterruptedException If the thread executing the request is interrupted.
*/
public boolean startScan() throws StartFullScanException, ExecutionException, InterruptedException, JsonProcessingException {
- // Fake properties read
- long maxHashExtractionFileSize = 1024 * 1024 * 2047; // 2GB - 1MB
+ // Read max hash extraction file size from config
+ long maxHashExtractionFileSize = AppConfig.MAX_HASH_EXTRACTION_FILE_SIZE;
+
String body = objectMapper.writeValueAsString(new StartScanAllRequest(maxHashExtractionFileSize));
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN, body);
@@ -75,8 +77,9 @@ public boolean startScan() throws StartFullScanException, ExecutionException, In
* @throws JsonProcessingException If an error occurs while serializing the start scan request to JSON.
*/
public boolean startScanById(long id) throws StartScanByIdException, ExecutionException, InterruptedException, JsonProcessingException {
- // Fake properties read
- long maxHashExtractionFileSize = 1024 * 1024 * 2047; // 2GB - 1MB
+ // Read max hash extraction file size from config
+ long maxHashExtractionFileSize = AppConfig.MAX_HASH_EXTRACTION_FILE_SIZE;
+
String body = objectMapper.writeValueAsString(new StartSingleScanRequest(id, maxHashExtractionFileSize));
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN_ID, body);
From 863cfaf0fa2f77ee2af573e1ad8e8bf7c27cb06e Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sat, 1 Nov 2025 15:40:23 +0100
Subject: [PATCH 04/44] GUI progress, storing settings left :)
---
.../org/pwss/controller/HomeController.java | 6 ++
.../java/org/pwss/view/screen/HomeScreen.form | 39 ++++++++++-
.../java/org/pwss/view/screen/HomeScreen.java | 64 ++++++++++++++++---
3 files changed, 100 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 6482af7..ae645bb 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -411,6 +411,12 @@ public void mouseClicked(MouseEvent e) {
}
});
+ screen.getMaxHashExctractionFileSizeSlider().addChangeListener(l -> {
+ int value = screen.getMaxHashExctractionFileSizeSlider().getValue();
+ log.debug("Setting max hash extraction file size to {} MB", value);
+ screen.getMaxHashExtractionFileSizeValueLabel().setText(value + " MB");
+ // TODO: Update the AppConfig value
+ });
}
@Override
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.form b/src/main/java/org/pwss/view/screen/HomeScreen.form
index 07a4cc5..eba3f55 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.form
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.form
@@ -495,7 +495,7 @@
-
+
@@ -511,6 +511,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index 7e137b3..a10d94d 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -17,6 +17,8 @@
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
@@ -211,6 +213,16 @@ public class HomeScreen extends BaseScreen {
*/
private JCheckBox showSplashScreenCheckBox;
+ /**
+ * Slider for setting the maximum file size for hash extraction.
+ */
+ private JSlider maxHashExctractionFileSizeSlider;
+
+ /**
+ * Label displaying the value of the maximum file size for hash extraction.
+ */
+ private JLabel maxHashExtractionFileSizeValueLabel;
+
@Override
protected String getScreenName() {
return "Home";
@@ -464,6 +476,24 @@ public JTable getQuarantineTable() {
return quarantineTable;
}
+ /**
+ * Returns the slider for setting the maximum file size for hash extraction.
+ *
+ * @return JSlider for maximum hash extraction file size.
+ */
+ public JLabel getMaxHashExtractionFileSizeValueLabel() {
+ return maxHashExtractionFileSizeValueLabel;
+ }
+
+ /**
+ * Returns the slider for setting the maximum file size for hash extraction.
+ *
+ * @return JSlider for maximum hash extraction file size.
+ */
+ public JSlider getMaxHashExctractionFileSizeSlider() {
+ return maxHashExctractionFileSizeSlider;
+ }
+
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
@@ -657,14 +687,32 @@ public JTable getQuarantineTable() {
label8.setText("© 2025 PWSS ORG");
panel5.add(label8, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel6 = new JPanel();
- panel6.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
+ panel6.setLayout(new GridLayoutManager(5, 1, new Insets(0, 0, 0, 0), -1, -1));
panel5.add(panel6, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
showSplashScreenCheckBox = new JCheckBox();
showSplashScreenCheckBox.setText("Show splash screen");
panel6.add(showSplashScreenCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ maxHashExctractionFileSizeSlider = new JSlider();
+ maxHashExctractionFileSizeSlider.setMajorTickSpacing(4096);
+ maxHashExctractionFileSizeSlider.setMaximum(102400);
+ maxHashExctractionFileSizeSlider.setMinimum(2048);
+ maxHashExctractionFileSizeSlider.setMinorTickSpacing(1024);
+ maxHashExctractionFileSizeSlider.setPaintLabels(false);
+ maxHashExctractionFileSizeSlider.setPaintTicks(true);
+ maxHashExctractionFileSizeSlider.setPaintTrack(true);
+ maxHashExctractionFileSizeSlider.setSnapToTicks(true);
+ panel6.add(maxHashExctractionFileSizeSlider, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label9 = new JLabel();
- label9.setText("Theme");
- panel5.add(label9, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ label9.setText("Max hash exctraction file size (MB)");
+ panel6.add(label9, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final JSeparator separator1 = new JSeparator();
+ panel6.add(separator1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ maxHashExtractionFileSizeValueLabel = new JLabel();
+ maxHashExtractionFileSizeValueLabel.setText("Selected:");
+ panel6.add(maxHashExtractionFileSizeValueLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final JLabel label10 = new JLabel();
+ label10.setText("Theme");
+ panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel7 = new JPanel();
panel7.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane5.setRightComponent(panel7);
@@ -672,11 +720,11 @@ public JTable getQuarantineTable() {
panel7.add(scrollPane9, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
quarantineTable = new JTable();
scrollPane9.setViewportView(quarantineTable);
- final JLabel label10 = new JLabel();
- Font label10Font = this.$$$getFont$$$(null, Font.BOLD, 14, label10.getFont());
- if (label10Font != null) label10.setFont(label10Font);
- label10.setText("Quarantined files");
- panel7.add(label10, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final JLabel label11 = new JLabel();
+ Font label11Font = this.$$$getFont$$$(null, Font.BOLD, 14, label11.getFont());
+ if (label11Font != null) label11.setFont(label11Font);
+ label11.setText("Quarantined files");
+ panel7.add(label11, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
scanProgressContainer = new JPanel();
scanProgressContainer.setLayout(new GridLayoutManager(1, 2, new Insets(10, 10, 10, 10), -1, -1));
rootPanel.add(scanProgressContainer, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
From 503d5b57be74f389a3f6ecd951b19eefeb8ef899 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sun, 2 Nov 2025 19:07:09 +0100
Subject: [PATCH 05/44] Replace HTTP 422 to 400 in service classes status code
handling
---
src/main/java/org/pwss/service/FileService.java | 8 ++++----
.../pwss/service/MonitoredDirectoryService.java | 5 ++---
src/main/java/org/pwss/service/NoteService.java | 4 ++--
.../java/org/pwss/service/ScanSummaryService.java | 14 +++++++-------
4 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/src/main/java/org/pwss/service/FileService.java b/src/main/java/org/pwss/service/FileService.java
index 5c5be7b..d1a4314 100644
--- a/src/main/java/org/pwss/service/FileService.java
+++ b/src/main/java/org/pwss/service/FileService.java
@@ -57,7 +57,7 @@ public FileService() {
* @return true if the file was successfully quarantined; false otherwise.
* @throws QuarantineFileException If the file cannot be quarantined due to
* various reasons such as
- * unauthorized access, unprocessable entity, or
+ * unauthorized access, bad request, or
* server errors.
* @throws JsonProcessingException If there is an error during JSON
* serialization or deserialization.
@@ -84,8 +84,8 @@ public boolean quarantineFile(long fileId) throws QuarantineFileException, JsonP
return switch (response.statusCode()) {
case 200 -> parsed.map(QuarantineResponse::successful).orElse(false);
+ case 400 -> throw new QuarantineFileException("File cannot be quarantined: " + fileId);
case 401 -> throw new QuarantineFileException("Unauthorized: Invalid credentials for file quarantine");
- case 422 -> throw new QuarantineFileException("File cannot be quarantined: " + fileId);
case 500 -> throw new QuarantineFileException("Server error during file quarantine");
default -> throw new QuarantineFileException("File quarantine failed: Unexpected status code ");
};
@@ -99,7 +99,7 @@ public boolean quarantineFile(long fileId) throws QuarantineFileException, JsonP
* @return A `QuarantineResponse` object containing the response details.
* @throws UnquarantineFileException If the file cannot be unquarantined due to
* various reasons such as
- * unauthorized access, unprocessable entity,
+ * unauthorized access, bad request,
* or server errors.
* @throws JsonProcessingException If there is an error during JSON
* serialization or deserialization.
@@ -125,8 +125,8 @@ public boolean unquarantineFile(QuarantineMetadata metadata) throws Unquarantine
return switch (response.statusCode()) {
case 200 -> true;
+ case 400 -> throw new UnquarantineFileException("File cannot be unquarantined: " + metadata.keyName());
case 401 -> throw new UnquarantineFileException("Unauthorized: Invalid credentials for file unquarantine");
- case 422 -> throw new UnquarantineFileException("File cannot be unquarantined: " + metadata.keyName());
case 500 -> throw new UnquarantineFileException("Server error during file unquarantine");
default -> throw new UnquarantineFileException("File unquarantine failed: Unexpected status code ");
};
diff --git a/src/main/java/org/pwss/service/MonitoredDirectoryService.java b/src/main/java/org/pwss/service/MonitoredDirectoryService.java
index 166c733..8477cf2 100644
--- a/src/main/java/org/pwss/service/MonitoredDirectoryService.java
+++ b/src/main/java/org/pwss/service/MonitoredDirectoryService.java
@@ -182,12 +182,11 @@ public boolean updateMonitoredDirectory(long id, boolean isActive, String notes,
return switch (response.statusCode()) {
case 200 -> true;
+ case 400 -> throw new UpdateMonitoredDirectoryException(
+ "Update monitored directory failed: invalid input data.");
case 401 ->
throw new UpdateMonitoredDirectoryException(
"Update monitored directory failed: User not authorized to perform this action.");
- case 422 ->
- throw new UpdateMonitoredDirectoryException(
- "Update monitored directory failed: Unprocessable entity - invalid input data.");
case 500 ->
throw new UpdateMonitoredDirectoryException(
"Update monitored directory failed: An error occurred on the server while attempting to update the monitored directory.");
diff --git a/src/main/java/org/pwss/service/NoteService.java b/src/main/java/org/pwss/service/NoteService.java
index 1018a9e..07a4e1d 100644
--- a/src/main/java/org/pwss/service/NoteService.java
+++ b/src/main/java/org/pwss/service/NoteService.java
@@ -38,8 +38,8 @@ public boolean updateNotes(long noteId, String newNotes) throws UpdateNoteExcept
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.NOTE_UPDATE, body);
return switch (response.statusCode()) {
case 200 -> true;
+ case 400 -> throw new UpdateNoteException("Update notes failed: Invalid note ID or note content.");
case 401 -> throw new UpdateNoteException("Update notes failed: User not authorized to perform this action.");
- case 422 -> throw new UpdateNoteException("Update notes failed: Invalid note ID or note content.");
case 500 -> throw new UpdateNoteException("Update notes failed: An error occurred on the server while attempting to update the notes.");
default -> false;
};
@@ -61,9 +61,9 @@ public boolean restoreNotes(long noteId, RestoreNoteType restoreNoteType) throws
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.NOTE_RESTORE, body);
return switch (response.statusCode()) {
case 200 -> true;
+ case 400 -> throw new RestoreNoteException("Restore notes failed: Invalid note ID or note content.");
case 401 ->
throw new RestoreNoteException("Restore notes failed: User not authorized to perform this action.");
- case 422 -> throw new RestoreNoteException("Restore notes failed: Invalid note ID or note content.");
case 500 ->
throw new RestoreNoteException("Restore notes failed: An error occurred on the server while attempting to restore the notes.");
default -> false;
diff --git a/src/main/java/org/pwss/service/ScanSummaryService.java b/src/main/java/org/pwss/service/ScanSummaryService.java
index bd883a7..ac99b5e 100644
--- a/src/main/java/org/pwss/service/ScanSummaryService.java
+++ b/src/main/java/org/pwss/service/ScanSummaryService.java
@@ -5,8 +5,8 @@
import java.net.http.HttpResponse;
import java.util.List;
import java.util.concurrent.ExecutionException;
-import org.pwss.exception.scan_summary.GetMostRecentSummaryException;
import org.pwss.exception.scan_summary.FileSearchException;
+import org.pwss.exception.scan_summary.GetMostRecentSummaryException;
import org.pwss.exception.scan_summary.GetSummaryForFileException;
import org.pwss.exception.scan_summary.GetSummaryForScanException;
import org.pwss.model.entity.File;
@@ -67,12 +67,12 @@ public List getSummaryForFile(long fileId) throws GetSummaryForFile
return switch (response.statusCode()) {
case 200 -> List.of(objectMapper.readValue(response.body(), ScanSummary[].class));
+ case 400 ->
+ throw new GetSummaryForFileException("Get summaries for file failed: The provided file ID is invalid.");
case 401 ->
throw new GetSummaryForFileException("Get summaries for file failed: User not authorized to perform this action.");
case 404 ->
throw new GetSummaryForFileException("Get summaries for file failed: No scan summaries found for the specified file.");
- case 422 ->
- throw new GetSummaryForFileException("Get summaries for file failed: The provided file ID is invalid.");
case 500 ->
throw new GetSummaryForFileException("Get summaries for file failed: An error occurred on the server while attempting to retrieve the scan summaries.");
default -> null;
@@ -96,11 +96,11 @@ public List searchFiles(String queryString, boolean ascending) throws File
return switch (response.statusCode()) {
case 200 -> List.of(objectMapper.readValue(response.body(), File[].class));
+ case 400 ->
+ throw new FileSearchException("Search files failed: The provided search parameters are invalid.");
case 401 ->
throw new FileSearchException("Search files failed: User not authorized to perform this action.");
case 404 -> List.of();
- case 422 ->
- throw new FileSearchException("Search files failed: The provided search parameters are invalid.");
case 500 ->
throw new FileSearchException("Search files failed: An error occurred on the server while attempting to search for files.");
default -> null;
@@ -113,12 +113,12 @@ public List getScanSummaryForScan(long scanId) throws GetSummaryFor
return switch (response.statusCode()) {
case 200 -> List.of(objectMapper.readValue(response.body(), ScanSummary[].class));
+ case 400 ->
+ throw new GetSummaryForScanException("Get summaries for scan failed: The provided scan ID is invalid.");
case 401 ->
throw new GetSummaryForScanException("Get summaries for scan failed: User not authorized to perform this action.");
case 404 ->
throw new GetSummaryForScanException("Get summaries for scan failed: No scan summaries found for the specified file.");
- case 422 ->
- throw new GetSummaryForScanException("Get summaries for scan failed: The provided scan ID is invalid.");
case 500 ->
throw new GetSummaryForScanException("Get summaries for scan failed: An error occurred on the server while attempting to retrieve the scan summaries.");
default -> null;
From 342a554ceabd55cdf6057fa139536901a49c33fc Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sun, 2 Nov 2025 19:44:26 +0100
Subject: [PATCH 06/44] Unlimited checkbox
---
.../org/pwss/controller/HomeController.java | 68 ++++++++++--
.../java/org/pwss/service/ScanService.java | 12 +--
.../java/org/pwss/utils/ConversionUtils.java | 100 ++++++++++++++++++
.../java/org/pwss/view/screen/HomeScreen.form | 14 ++-
.../java/org/pwss/view/screen/HomeScreen.java | 23 +++-
src/main/resources/logback.xml | 2 +-
6 files changed, 197 insertions(+), 22 deletions(-)
create mode 100644 src/main/java/org/pwss/utils/ConversionUtils.java
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index ae645bb..487fa0a 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -13,7 +13,6 @@
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
-
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JList;
@@ -61,6 +60,7 @@
import org.pwss.service.ScanService;
import org.pwss.service.ScanSummaryService;
import org.pwss.utils.AppTheme;
+import org.pwss.utils.ConversionUtils;
import org.pwss.utils.LiveFeedUtils;
import org.pwss.utils.MonitoredDirectoryUtils;
import org.pwss.utils.OSUtils;
@@ -71,7 +71,9 @@
import org.pwss.view.screen.HomeScreen;
import org.slf4j.LoggerFactory;
+
import static org.pwss.app_settings.AppConfig.APP_THEME;
+import static org.pwss.app_settings.AppConfig.MAX_HASH_EXTRACTION_FILE_SIZE;
import static org.pwss.app_settings.AppConfig.USE_SPLASH_SCREEN;
// TODO: NEEDS REFACTORING - VERY LARGE CLASS
@@ -167,6 +169,11 @@ public final class HomeController extends BaseController {
*/
private boolean showSplashScreenSetting;
+ /**
+ * Maximum file size (in bytes) for which hash extraction will be performed.
+ */
+ private long maxFileSizeForHashExtraction;
+
/**
* Constructor to initialize HomeController with a HomeScreen view instance.
*
@@ -183,6 +190,7 @@ public HomeController(HomeScreen view) {
this.monitoredDirectoryPopupFactory = new MonitoredDirectoryPopupFactory(
new MonitoredDirectoryPopupListenerImpl(this, monitoredDirectoryService, noteService));
this.showSplashScreenSetting = USE_SPLASH_SCREEN;
+ this.maxFileSizeForHashExtraction = MAX_HASH_EXTRACTION_FILE_SIZE;
}
@Override
@@ -412,10 +420,43 @@ public void mouseClicked(MouseEvent e) {
}
});
screen.getMaxHashExctractionFileSizeSlider().addChangeListener(l -> {
- int value = screen.getMaxHashExctractionFileSizeSlider().getValue();
- log.debug("Setting max hash extraction file size to {} MB", value);
- screen.getMaxHashExtractionFileSizeValueLabel().setText(value + " MB");
- // TODO: Update the AppConfig value
+ long valueMegabytes = screen.getMaxHashExctractionFileSizeSlider().getValue();
+ log.debug("Setting max hash extraction file size to {} MB", valueMegabytes);
+ long valueBytes = ConversionUtils.megabytesToBytes(valueMegabytes);
+
+ // Set App config value to size in bytes
+ if (AppConfig.setMaxHashExtractionFileSize(valueBytes)) {
+ screen.getMaxHashExtractionFileSizeValueLabel().setText(valueMegabytes + " MB");
+ maxFileSizeForHashExtraction = ConversionUtils.megabytesToBytes(valueMegabytes);
+ } else {
+ log.error("Failed to update max hash extraction file size in app config.");
+ }
+ });
+ screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().addActionListener(l -> {
+ // If checked set the max size to -1L
+ boolean checked = screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().isSelected();
+ if (checked) {
+ log.debug("Setting max hash extraction file size to unlimited.");
+ if (AppConfig.setMaxHashExtractionFileSize(-1L)) {
+ maxFileSizeForHashExtraction = -1L;
+ screen.getMaxHashExtractionFileSizeValueLabel().setText("Unlimited");
+ screen.getMaxHashExctractionFileSizeSlider().setEnabled(false);
+ } else {
+ log.error("Failed to update max hash extraction file size in app config.");
+ }
+ } else {
+ // If unchecked set the size to the current slider value
+ long sliderValueMegabytes = screen.getMaxHashExctractionFileSizeSlider().getValue();
+ log.debug("Setting max hash extraction file size to {} MB", sliderValueMegabytes);
+ long sliderValueBytes = ConversionUtils.megabytesToBytes(sliderValueMegabytes);
+ if (AppConfig.setMaxHashExtractionFileSize(sliderValueBytes)) {
+ maxFileSizeForHashExtraction = sliderValueBytes;
+ screen.getMaxHashExtractionFileSizeValueLabel().setText(sliderValueMegabytes + " MB");
+ screen.getMaxHashExctractionFileSizeSlider().setEnabled(true);
+ } else {
+ log.error("Failed to update max hash extraction file size in app config.");
+ }
+ }
});
}
@@ -556,6 +597,19 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
}
});
}));
+
+ if (maxFileSizeForHashExtraction != -1L) {
+ screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().setSelected(false);
+ screen.getMaxHashExctractionFileSizeSlider().setEnabled(true);
+
+ final int maxSliderValueMegabytes = Math.toIntExact(ConversionUtils.bytesToMegabytes(maxFileSizeForHashExtraction));
+ screen.getMaxHashExctractionFileSizeSlider().setValue(maxSliderValueMegabytes);
+ screen.getMaxHashExtractionFileSizeValueLabel().setText(maxSliderValueMegabytes + " MB");
+ } else {
+ screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().setSelected(true);
+ screen.getMaxHashExctractionFileSizeSlider().setEnabled(false);
+ screen.getMaxHashExtractionFileSizeValueLabel().setText("Unlimited");
+ }
}
/**
@@ -601,7 +655,7 @@ public void performStartScan(boolean singleDirectory) {
int modelRow = table.convertRowIndexToModel(viewRow);
Optional dir = model.getDirectoryAt(modelRow);
if (dir.isPresent()) {
- startScanSuccess = scanService.startScanById(dir.get().id());
+ startScanSuccess = scanService.startScanById(dir.get().id(), maxFileSizeForHashExtraction);
scanningDirs.add(dir.get());
baseLineScan = !dir.get().baselineEstablished();
} else {
@@ -611,7 +665,7 @@ public void performStartScan(boolean singleDirectory) {
}
} else {
baseLineScan = false;
- startScanSuccess = scanService.startScan();
+ startScanSuccess = scanService.startScan(maxFileSizeForHashExtraction);
scanningDirs.addAll(allMonitoredDirectories);
}
SwingUtilities.invokeLater(() -> {
diff --git a/src/main/java/org/pwss/service/ScanService.java b/src/main/java/org/pwss/service/ScanService.java
index c297f41..2411a0d 100644
--- a/src/main/java/org/pwss/service/ScanService.java
+++ b/src/main/java/org/pwss/service/ScanService.java
@@ -41,15 +41,13 @@ public ScanService() {
/**
* Starts a scan by sending a request to the START_SCAN endpoint.
*
+ * @param maxHashExtractionFileSize The maximum file size for hash extraction.
* @return `true` if the scan start request is successful, otherwise `false`.
* @throws StartFullScanException If the scan start attempt fails due to various reasons such as invalid credentials, no active monitored directories, scan already running, or server error.
* @throws ExecutionException If an error occurs during the asynchronous execution of the request.
* @throws InterruptedException If the thread executing the request is interrupted.
*/
- public boolean startScan() throws StartFullScanException, ExecutionException, InterruptedException, JsonProcessingException {
- // Read max hash extraction file size from config
- long maxHashExtractionFileSize = AppConfig.MAX_HASH_EXTRACTION_FILE_SIZE;
-
+ public boolean startScan(long maxHashExtractionFileSize) throws StartFullScanException, ExecutionException, InterruptedException, JsonProcessingException {
String body = objectMapper.writeValueAsString(new StartScanAllRequest(maxHashExtractionFileSize));
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN, body);
@@ -70,16 +68,14 @@ public boolean startScan() throws StartFullScanException, ExecutionException, In
* Starts a scan for a specific monitored directory by its ID by sending a request to the START_SCAN_ID endpoint.
*
* @param id The ID of the monitored directory to start the scan for.
+ * @param maxHashExtractionFileSize The maximum file size for hash extraction.
* @return `true` if the scan start request is successful, otherwise `false`.
* @throws StartScanByIdException If the scan start attempt fails due to various reasons such as invalid credentials, monitored directory not found, monitored directory inactive, scan already running, or server error.
* @throws ExecutionException If an error occurs during the asynchronous execution of the request.
* @throws InterruptedException If the thread executing the request is interrupted.
* @throws JsonProcessingException If an error occurs while serializing the start scan request to JSON.
*/
- public boolean startScanById(long id) throws StartScanByIdException, ExecutionException, InterruptedException, JsonProcessingException {
- // Read max hash extraction file size from config
- long maxHashExtractionFileSize = AppConfig.MAX_HASH_EXTRACTION_FILE_SIZE;
-
+ public boolean startScanById(long id, long maxHashExtractionFileSize) throws StartScanByIdException, ExecutionException, InterruptedException, JsonProcessingException {
String body = objectMapper.writeValueAsString(new StartSingleScanRequest(id, maxHashExtractionFileSize));
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.START_SCAN_ID, body);
diff --git a/src/main/java/org/pwss/utils/ConversionUtils.java b/src/main/java/org/pwss/utils/ConversionUtils.java
new file mode 100644
index 0000000..3c1e1a4
--- /dev/null
+++ b/src/main/java/org/pwss/utils/ConversionUtils.java
@@ -0,0 +1,100 @@
+package org.pwss.utils;
+
+/**
+ * Utility class providing methods to convert between different units of data
+ * measurement, such as bytes,
+ * megabytes, and gigabytes. This class includes static utility methods that
+ * perform these conversions.
+ *
+ *
+ * The primary purpose of this class is to facilitate unit conversions in
+ * scenarios where file sizes or
+ * other data measurements need to be handled in a specific unit for easier
+ * comprehension or processing.
+ *
+ */
+public class ConversionUtils {
+ private static final long MEGABYTE = 1024L * 1024L;
+ private static final long GIGABYTE = MEGABYTE * 1024L;
+
+ /**
+ * Converts a value in bytes to megabytes.
+ *
+ * @param bytes the number of bytes to convert
+ * @return the equivalent value in megabytes as a Long
+ */
+ public static Long bytesToMegabytes(Long bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException("Bytes value cannot be null.");
+ }
+ return bytes / MEGABYTE;
+ }
+
+ /**
+ * Converts a value in megabytes to bytes.
+ *
+ * @param megabytes the number of megabytes to convert
+ * @return the equivalent value in bytes as a Long
+ */
+ public static Long megabytesToBytes(Long megabytes) {
+ if (megabytes == null) {
+ throw new IllegalArgumentException("Megabytes value cannot be null.");
+ }
+ return megabytes * MEGABYTE;
+ }
+
+ // Optional: If you want to handle conversion with double values for more
+ // precise calculations
+ /**
+ * Converts a value in bytes to megabytes.
+ *
+ * @param bytes the number of bytes to convert
+ * @return the equivalent value in megabytes as a Double
+ */
+ public static Double bytesToMegabytesDouble(Long bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException("Bytes value cannot be null.");
+ }
+ return (double) bytes / MEGABYTE;
+ }
+
+ /**
+ * Converts a value in megabytes to bytes.
+ *
+ * @param megabytes the number of megabytes to convert
+ * @return the equivalent value in bytes as a Double
+ */
+ public static Long megabytesToBytesDouble(Double megabytes) {
+ if (megabytes == null) {
+ throw new IllegalArgumentException("Megabytes value cannot be null.");
+ }
+ return Math.round(megabytes * MEGABYTE);
+ }
+
+ /**
+ * Converts a value in bytes to gigabytes.
+ *
+ * @param bytes the number of bytes to convert
+ * @return the equivalent value in gigabytes as a Double
+ */
+ public static Double bytesToGigabytesDouble(Long bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException("Bytes value cannot be null.");
+ }
+ return (double) bytes / GIGABYTE;
+ }
+
+ /**
+ * Converts a value in gigabytes to bytes.
+ *
+ * @param gigabytes the number of gigabytes to convert
+ * @return the equivalent value in bytes as a Double
+ */
+ public static Long gigabytesToBytesDouble(Double gigabytes) {
+ if (gigabytes == null) {
+ throw new IllegalArgumentException("Gigabytes value cannot be null.");
+ }
+ return Math.round(gigabytes * GIGABYTE);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.form b/src/main/java/org/pwss/view/screen/HomeScreen.form
index eba3f55..d81e88d 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.form
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.form
@@ -495,7 +495,7 @@
-
+
@@ -516,9 +516,9 @@
-
+
-
+
@@ -548,6 +548,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index a10d94d..20840c0 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -223,6 +223,11 @@ public class HomeScreen extends BaseScreen {
*/
private JLabel maxHashExtractionFileSizeValueLabel;
+ /**
+ * Checkbox to set the maximum hash extraction file size to unlimited.
+ */
+ private JCheckBox maxHashExctractionFileSizeUnlimitedCheckbox;
+
@Override
protected String getScreenName() {
return "Home";
@@ -494,6 +499,15 @@ public JSlider getMaxHashExctractionFileSizeSlider() {
return maxHashExctractionFileSizeSlider;
}
+ /**
+ * Returns the checkbox to set the maximum hash extraction file size to unlimited.
+ *
+ * @return JCheckBox representing the "Unlimited" option for max hash extraction file size.
+ */
+ public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
+ return maxHashExctractionFileSizeUnlimitedCheckbox;
+ }
+
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
@@ -687,15 +701,15 @@ public JSlider getMaxHashExctractionFileSizeSlider() {
label8.setText("© 2025 PWSS ORG");
panel5.add(label8, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel6 = new JPanel();
- panel6.setLayout(new GridLayoutManager(5, 1, new Insets(0, 0, 0, 0), -1, -1));
+ panel6.setLayout(new GridLayoutManager(6, 1, new Insets(0, 0, 0, 0), -1, -1));
panel5.add(panel6, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
showSplashScreenCheckBox = new JCheckBox();
showSplashScreenCheckBox.setText("Show splash screen");
panel6.add(showSplashScreenCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
maxHashExctractionFileSizeSlider = new JSlider();
- maxHashExctractionFileSizeSlider.setMajorTickSpacing(4096);
+ maxHashExctractionFileSizeSlider.setMajorTickSpacing(5120);
maxHashExctractionFileSizeSlider.setMaximum(102400);
- maxHashExctractionFileSizeSlider.setMinimum(2048);
+ maxHashExctractionFileSizeSlider.setMinimum(100);
maxHashExctractionFileSizeSlider.setMinorTickSpacing(1024);
maxHashExctractionFileSizeSlider.setPaintLabels(false);
maxHashExctractionFileSizeSlider.setPaintTicks(true);
@@ -710,6 +724,9 @@ public JSlider getMaxHashExctractionFileSizeSlider() {
maxHashExtractionFileSizeValueLabel = new JLabel();
maxHashExtractionFileSizeValueLabel.setText("Selected:");
panel6.add(maxHashExtractionFileSizeValueLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ maxHashExctractionFileSizeUnlimitedCheckbox = new JCheckBox();
+ maxHashExctractionFileSizeUnlimitedCheckbox.setText("Unlimited");
+ panel6.add(maxHashExctractionFileSizeUnlimitedCheckbox, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label10 = new JLabel();
label10.setText("Theme");
panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 83566ff..bf24e35 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -11,7 +11,7 @@
-
+
\ No newline at end of file
From fca5da15b3181f947d2369cb1807e9f7552dc182 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sun, 2 Nov 2025 19:55:33 +0100
Subject: [PATCH 07/44] Add safeguard for not allowing /dev & /proc to be set
as monitored directories on unix based systems
---
.../controller/NewDirectoryController.java | 28 ++++++++++++-------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/src/main/java/org/pwss/controller/NewDirectoryController.java b/src/main/java/org/pwss/controller/NewDirectoryController.java
index ad5da69..35f3c74 100644
--- a/src/main/java/org/pwss/controller/NewDirectoryController.java
+++ b/src/main/java/org/pwss/controller/NewDirectoryController.java
@@ -5,6 +5,7 @@
import org.pwss.navigation.NavigationEvents;
import org.pwss.navigation.Screen;
import org.pwss.service.MonitoredDirectoryService;
+import org.pwss.utils.OSUtils;
import org.pwss.utils.StringConstants;
import org.pwss.view.screen.NewDirectoryScreen;
@@ -42,16 +43,23 @@ public void onShow() {
@Override
void initListeners() {
- getScreen().getSelectPathButton().addActionListener(e -> openFolderPicker());
- getScreen().getCancelButton().addActionListener(e -> NavigationEvents.navigateTo(Screen.HOME));
- getScreen().getCreateButton().addActionListener(e -> createNewDirectory());
+ screen.getSelectPathButton().addActionListener(e -> openFolderPicker());
+ screen.getCancelButton().addActionListener(e -> NavigationEvents.navigateTo(Screen.HOME));
+ screen.getCreateButton().addActionListener(e -> createNewDirectory());
}
@Override
void refreshView() {
- getScreen().getPathLabel()
- .setText(selectedPath != null ? selectedPath : StringConstants.NEW_DIR_NO_PATH_SELECTED);
- getScreen().getCreateButton().setEnabled(selectedPath != null && !selectedPath.isEmpty());
+ screen.getPathLabel().setText(selectedPath != null ? selectedPath : StringConstants.NEW_DIR_NO_PATH_SELECTED);
+ screen.getCreateButton().setEnabled(selectedPath != null && !selectedPath.isEmpty());
+
+ // Additional check for Unix-based systems to disable creation for /dev and /proc paths
+ if (OSUtils.isUnix() && selectedPath != null) {
+ if (selectedPath.startsWith("/dev") || selectedPath.startsWith("/proc")) {
+ screen.getCreateButton().setEnabled(false);
+ screen.showError("Cannot monitor directories under /dev or /proc on Unix-based systems.");
+ }
+ }
}
/**
@@ -74,16 +82,16 @@ private void openFolderPicker() {
* Navigates back to the home screen upon successful creation.
*/
private void createNewDirectory() {
- boolean includeSubdirectories = getScreen().getIncludeSubdirectoriesCheckBox().isSelected();
- boolean makeActive = getScreen().getMakeDirectoryActiveCheckBox().isSelected();
+ boolean includeSubdirectories = screen.getIncludeSubdirectoriesCheckBox().isSelected();
+ boolean makeActive = screen.getMakeDirectoryActiveCheckBox().isSelected();
if (selectedPath != null && !selectedPath.isEmpty()) {
try {
monitoredDirectoryService.createNewMonitoredDirectory(selectedPath, includeSubdirectories, makeActive);
- JOptionPane.showMessageDialog(getScreen().getRootPanel(), StringConstants.NEW_DIR_SUCCESS_TEXT,
+ JOptionPane.showMessageDialog(screen.getRootPanel(), StringConstants.NEW_DIR_SUCCESS_TEXT,
StringConstants.GENERIC_SUCCESS, JOptionPane.INFORMATION_MESSAGE);
NavigationEvents.navigateTo(Screen.HOME);
} catch (Exception e) {
- JOptionPane.showMessageDialog(getScreen().getRootPanel(),
+ JOptionPane.showMessageDialog(screen.getRootPanel(),
StringConstants.NEW_DIR_ERROR_PREFIX + e.getMessage(), StringConstants.GENERIC_ERROR,
JOptionPane.ERROR_MESSAGE);
}
From 8064b72dfb5350ceaa1111c0ccd834d567a96a98 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sun, 2 Nov 2025 19:56:32 +0100
Subject: [PATCH 08/44] Bump to 1.1 :)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 432db6f..7f23fdd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
org.pwss
integrity_hash
jar
- 1.0
+ 1.1
A GUI application for file integrity checking
From 34fee1dbd334a9a02c7fcd111764ad72d195f82c Mon Sep 17 00:00:00 2001
From: Peter Westin <83552499+pwgit-create@users.noreply.github.com>
Date: Sun, 2 Nov 2025 20:27:52 +0100
Subject: [PATCH 09/44] Update
src/main/java/org/pwss/view/screen/HomeScreen.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/main/java/org/pwss/view/screen/HomeScreen.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index 20840c0..b9daa59 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -226,7 +226,7 @@ public class HomeScreen extends BaseScreen {
/**
* Checkbox to set the maximum hash extraction file size to unlimited.
*/
- private JCheckBox maxHashExctractionFileSizeUnlimitedCheckbox;
+ private JCheckBox maxHashExtractionFileSizeUnlimitedCheckbox;
@Override
protected String getScreenName() {
From cbb8316496daf7be5234f103663b9da2dbaf27a9 Mon Sep 17 00:00:00 2001
From: Peter Westin <83552499+pwgit-create@users.noreply.github.com>
Date: Sun, 2 Nov 2025 20:28:23 +0100
Subject: [PATCH 10/44] Update
src/main/java/org/pwss/view/screen/HomeScreen.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../java/org/pwss/view/screen/HomeScreen.java | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index b9daa59..4430523 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -706,15 +706,15 @@ public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
showSplashScreenCheckBox = new JCheckBox();
showSplashScreenCheckBox.setText("Show splash screen");
panel6.add(showSplashScreenCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- maxHashExctractionFileSizeSlider = new JSlider();
- maxHashExctractionFileSizeSlider.setMajorTickSpacing(5120);
- maxHashExctractionFileSizeSlider.setMaximum(102400);
- maxHashExctractionFileSizeSlider.setMinimum(100);
- maxHashExctractionFileSizeSlider.setMinorTickSpacing(1024);
- maxHashExctractionFileSizeSlider.setPaintLabels(false);
- maxHashExctractionFileSizeSlider.setPaintTicks(true);
- maxHashExctractionFileSizeSlider.setPaintTrack(true);
- maxHashExctractionFileSizeSlider.setSnapToTicks(true);
+ maxHashExtractionFileSizeSlider = new JSlider();
+ maxHashExtractionFileSizeSlider.setMajorTickSpacing(5120);
+ maxHashExtractionFileSizeSlider.setMaximum(102400);
+ maxHashExtractionFileSizeSlider.setMinimum(100);
+ maxHashExtractionFileSizeSlider.setMinorTickSpacing(1024);
+ maxHashExtractionFileSizeSlider.setPaintLabels(false);
+ maxHashExtractionFileSizeSlider.setPaintTicks(true);
+ maxHashExtractionFileSizeSlider.setPaintTrack(true);
+ maxHashExtractionFileSizeSlider.setSnapToTicks(true);
panel6.add(maxHashExctractionFileSizeSlider, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label9 = new JLabel();
label9.setText("Max hash exctraction file size (MB)");
From ed4c6acd010adb16d2ee5a85228da8edf5f29008 Mon Sep 17 00:00:00 2001
From: Peter Westin <83552499+pwgit-create@users.noreply.github.com>
Date: Sun, 2 Nov 2025 20:28:40 +0100
Subject: [PATCH 11/44] Update
src/main/java/org/pwss/controller/HomeController.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/main/java/org/pwss/controller/HomeController.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 487fa0a..075edcd 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -419,8 +419,8 @@ public void mouseClicked(MouseEvent e) {
}
});
- screen.getMaxHashExctractionFileSizeSlider().addChangeListener(l -> {
- long valueMegabytes = screen.getMaxHashExctractionFileSizeSlider().getValue();
+ screen.getMaxHashExtractionFileSizeSlider().addChangeListener(l -> {
+ long valueMegabytes = screen.getMaxHashExtractionFileSizeSlider().getValue();
log.debug("Setting max hash extraction file size to {} MB", valueMegabytes);
long valueBytes = ConversionUtils.megabytesToBytes(valueMegabytes);
From 43858eb28eda75d2a1a9d1116a2d46962d3da5bf Mon Sep 17 00:00:00 2001
From: Peter Westin <83552499+pwgit-create@users.noreply.github.com>
Date: Sun, 2 Nov 2025 20:28:56 +0100
Subject: [PATCH 12/44] Update
src/main/java/org/pwss/controller/HomeController.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/main/java/org/pwss/controller/HomeController.java | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 075edcd..f259985 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -599,15 +599,15 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
}));
if (maxFileSizeForHashExtraction != -1L) {
- screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().setSelected(false);
- screen.getMaxHashExctractionFileSizeSlider().setEnabled(true);
+ screen.getMaxHashExtractionFileSizeUnlimitedCheckbox().setSelected(false);
+ screen.getMaxHashExtractionFileSizeSlider().setEnabled(true);
final int maxSliderValueMegabytes = Math.toIntExact(ConversionUtils.bytesToMegabytes(maxFileSizeForHashExtraction));
- screen.getMaxHashExctractionFileSizeSlider().setValue(maxSliderValueMegabytes);
+ screen.getMaxHashExtractionFileSizeSlider().setValue(maxSliderValueMegabytes);
screen.getMaxHashExtractionFileSizeValueLabel().setText(maxSliderValueMegabytes + " MB");
} else {
- screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().setSelected(true);
- screen.getMaxHashExctractionFileSizeSlider().setEnabled(false);
+ screen.getMaxHashExtractionFileSizeUnlimitedCheckbox().setSelected(true);
+ screen.getMaxHashExtractionFileSizeSlider().setEnabled(false);
screen.getMaxHashExtractionFileSizeValueLabel().setText("Unlimited");
}
}
From bcfed1a4e25fc5179e68d637f7598371b5903aec Mon Sep 17 00:00:00 2001
From: Peter Westin <83552499+pwgit-create@users.noreply.github.com>
Date: Sun, 2 Nov 2025 20:29:05 +0100
Subject: [PATCH 13/44] Update
src/main/java/org/pwss/app_settings/ConfigLoader.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/main/java/org/pwss/app_settings/ConfigLoader.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/app_settings/ConfigLoader.java b/src/main/java/org/pwss/app_settings/ConfigLoader.java
index f197389..859fb9d 100644
--- a/src/main/java/org/pwss/app_settings/ConfigLoader.java
+++ b/src/main/java/org/pwss/app_settings/ConfigLoader.java
@@ -326,7 +326,7 @@ final boolean setMaxHashExtractionFileSize(String maxHashExtractionFileSize) {
return true;
} catch (Exception exception) {
log.debug("Max hash extraction file size could not be set in app.config file", exception);
- log.error("Max hash extraction file size could not be set in app.config file", exception.getMessage());
+ log.error("Max hash extraction file size could not be set in app.config file: {}", exception.getMessage());
return false;
}
}
From 62e3e05dfc159b1157a5ded4adaabde1ab64bcda Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Sun, 2 Nov 2025 20:51:54 +0100
Subject: [PATCH 14/44] Spelling error correction
---
.../org/pwss/controller/HomeController.java | 10 ++++----
.../java/org/pwss/view/screen/HomeScreen.java | 25 +++++++++++--------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index f259985..645209c 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -432,27 +432,27 @@ public void mouseClicked(MouseEvent e) {
log.error("Failed to update max hash extraction file size in app config.");
}
});
- screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().addActionListener(l -> {
+ screen.getMaxHashExtractionFileSizeUnlimitedCheckbox().addActionListener(l -> {
// If checked set the max size to -1L
- boolean checked = screen.getMaxHashExctractionFileSizeUnlimitedCheckbox().isSelected();
+ boolean checked = screen.getMaxHashExtractionFileSizeUnlimitedCheckbox().isSelected();
if (checked) {
log.debug("Setting max hash extraction file size to unlimited.");
if (AppConfig.setMaxHashExtractionFileSize(-1L)) {
maxFileSizeForHashExtraction = -1L;
screen.getMaxHashExtractionFileSizeValueLabel().setText("Unlimited");
- screen.getMaxHashExctractionFileSizeSlider().setEnabled(false);
+ screen.getMaxHashExtractionFileSizeSlider().setEnabled(false);
} else {
log.error("Failed to update max hash extraction file size in app config.");
}
} else {
// If unchecked set the size to the current slider value
- long sliderValueMegabytes = screen.getMaxHashExctractionFileSizeSlider().getValue();
+ long sliderValueMegabytes = screen.getMaxHashExtractionFileSizeSlider().getValue();
log.debug("Setting max hash extraction file size to {} MB", sliderValueMegabytes);
long sliderValueBytes = ConversionUtils.megabytesToBytes(sliderValueMegabytes);
if (AppConfig.setMaxHashExtractionFileSize(sliderValueBytes)) {
maxFileSizeForHashExtraction = sliderValueBytes;
screen.getMaxHashExtractionFileSizeValueLabel().setText(sliderValueMegabytes + " MB");
- screen.getMaxHashExctractionFileSizeSlider().setEnabled(true);
+ screen.getMaxHashExtractionFileSizeSlider().setEnabled(true);
} else {
log.error("Failed to update max hash extraction file size in app config.");
}
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index 4430523..4c93b9c 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -4,6 +4,9 @@
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Spacer;
+
+import static org.pwss.app_settings.AppConfig.APP_THEME;
+
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
@@ -216,7 +219,7 @@ public class HomeScreen extends BaseScreen {
/**
* Slider for setting the maximum file size for hash extraction.
*/
- private JSlider maxHashExctractionFileSizeSlider;
+ private JSlider maxHashExtractionFileSizeSlider;
/**
* Label displaying the value of the maximum file size for hash extraction.
@@ -495,8 +498,8 @@ public JLabel getMaxHashExtractionFileSizeValueLabel() {
*
* @return JSlider for maximum hash extraction file size.
*/
- public JSlider getMaxHashExctractionFileSizeSlider() {
- return maxHashExctractionFileSizeSlider;
+ public JSlider getMaxHashExtractionFileSizeSlider() {
+ return maxHashExtractionFileSizeSlider;
}
/**
@@ -504,8 +507,8 @@ public JSlider getMaxHashExctractionFileSizeSlider() {
*
* @return JCheckBox representing the "Unlimited" option for max hash extraction file size.
*/
- public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
- return maxHashExctractionFileSizeUnlimitedCheckbox;
+ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
+ return maxHashExtractionFileSizeUnlimitedCheckbox;
}
{
@@ -564,7 +567,7 @@ public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane2 = new JScrollPane();
panel2.add(scrollPane2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
- monitoredDirectoryList = new JList();
+ monitoredDirectoryList = new JList();
monitoredDirectoryList.setEnabled(true);
monitoredDirectoryList.setSelectionMode(0);
monitoredDirectoryList.setToolTipText("Monitored directories list");
@@ -692,7 +695,7 @@ public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
panel5.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final Spacer spacer1 = new Spacer();
panel5.add(spacer1, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
- themePicker = new JComboBox();
+ themePicker = new JComboBox();
panel5.add(themePicker, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
restartButton = new JButton();
restartButton.setText("Restart");
@@ -715,7 +718,7 @@ public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
maxHashExtractionFileSizeSlider.setPaintTicks(true);
maxHashExtractionFileSizeSlider.setPaintTrack(true);
maxHashExtractionFileSizeSlider.setSnapToTicks(true);
- panel6.add(maxHashExctractionFileSizeSlider, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(maxHashExtractionFileSizeSlider, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label9 = new JLabel();
label9.setText("Max hash exctraction file size (MB)");
panel6.add(label9, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
@@ -724,9 +727,9 @@ public JCheckBox getMaxHashExctractionFileSizeUnlimitedCheckbox() {
maxHashExtractionFileSizeValueLabel = new JLabel();
maxHashExtractionFileSizeValueLabel.setText("Selected:");
panel6.add(maxHashExtractionFileSizeValueLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- maxHashExctractionFileSizeUnlimitedCheckbox = new JCheckBox();
- maxHashExctractionFileSizeUnlimitedCheckbox.setText("Unlimited");
- panel6.add(maxHashExctractionFileSizeUnlimitedCheckbox, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ maxHashExtractionFileSizeUnlimitedCheckbox = new JCheckBox();
+ maxHashExtractionFileSizeUnlimitedCheckbox.setText("Unlimited");
+ panel6.add(maxHashExtractionFileSizeUnlimitedCheckbox, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label10 = new JLabel();
label10.setText("Theme");
panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
From 32c9260265ebae0a528e17a26af1349e1e58a304 Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Sun, 2 Nov 2025 20:56:54 +0100
Subject: [PATCH 15/44] Corrected grammar error no.2
---
.../java/org/pwss/view/screen/HomeScreen.form | 6 +-
.../java/org/pwss/view/screen/HomeScreen.java | 286 +++++++++++++-----
2 files changed, 220 insertions(+), 72 deletions(-)
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.form b/src/main/java/org/pwss/view/screen/HomeScreen.form
index d81e88d..9c6ae80 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.form
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.form
@@ -511,7 +511,7 @@
-
+
@@ -531,7 +531,7 @@
-
+
@@ -548,7 +548,7 @@
-
+
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index 4c93b9c..bda50d4 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -1,12 +1,9 @@
package org.pwss.view.screen;
-
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Spacer;
-import static org.pwss.app_settings.AppConfig.APP_THEME;
-
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
@@ -33,7 +30,6 @@
import org.pwss.model.entity.MonitoredDirectory;
import org.pwss.utils.AppTheme;
-
/**
* The HomeScreen class represents the main screen of the application.
* It extends BaseScreen and contains various UI components that make up
@@ -47,7 +43,8 @@ public class HomeScreen extends BaseScreen {
private JPanel rootPanel;
/**
- * A tabbed pane that allows switching between different tabs in the home screen.
+ * A tabbed pane that allows switching between different tabs in the home
+ * screen.
*/
private JTabbedPane tabbedPane;
@@ -503,18 +500,20 @@ public JSlider getMaxHashExtractionFileSizeSlider() {
}
/**
- * Returns the checkbox to set the maximum hash extraction file size to unlimited.
+ * Returns the checkbox to set the maximum hash extraction file size to
+ * unlimited.
*
- * @return JCheckBox representing the "Unlimited" option for max hash extraction file size.
+ * @return JCheckBox representing the "Unlimited" option for max hash extraction
+ * file size.
*/
public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
return maxHashExtractionFileSizeUnlimitedCheckbox;
}
{
-// GUI initializer generated by IntelliJ IDEA GUI Designer
-// >>> IMPORTANT!! <<<
-// DO NOT EDIT OR ADD ANY CODE HERE!
+ // GUI initializer generated by IntelliJ IDEA GUI Designer
+ // >>> IMPORTANT!! <<<
+ // DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}
@@ -530,29 +529,49 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
rootPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
tabbedPane = new JTabbedPane();
Font tabbedPaneFont = this.$$$getFont$$$(null, -1, 14, tabbedPane.getFont());
- if (tabbedPaneFont != null) tabbedPane.setFont(tabbedPaneFont);
+ if (tabbedPaneFont != null)
+ tabbedPane.setFont(tabbedPaneFont);
tabbedPane.setTabPlacement(1);
tabbedPane.setToolTipText("");
- rootPanel.add(tabbedPane, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ rootPanel.add(tabbedPane,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_FIXED,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
+ 0, false));
homeTab = new JPanel();
homeTab.setLayout(new GridLayoutManager(4, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("\uD83C\uDFE0 Home", homeTab);
newDirectoryButton = new JButton();
newDirectoryButton.setText("New directory");
- homeTab.add(newDirectoryButton, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ homeTab.add(newDirectoryButton,
+ new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JSplitPane splitPane1 = new JSplitPane();
- homeTab.add(splitPane1, new GridConstraints(1, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
+ homeTab.add(splitPane1,
+ new GridConstraints(1, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
+ new Dimension(200, 200), null, 0, false));
final JPanel panel1 = new JPanel();
panel1.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane1.setLeftComponent(panel1);
final JLabel label1 = new JLabel();
Font label1Font = this.$$$getFont$$$(null, Font.BOLD, 16, label1.getFont());
- if (label1Font != null) label1.setFont(label1Font);
+ if (label1Font != null)
+ label1.setFont(label1Font);
label1.setText("Most recent scans");
- panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel1.add(label1,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane1 = new JScrollPane();
scrollPane1.setToolTipText("Double click a scan to see details");
- panel1.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ panel1.add(scrollPane1,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null,
+ 0, false));
recentScanTable = new JTable();
recentScanTable.setAutoResizeMode(2);
recentScanTable.setShowHorizontalLines(true);
@@ -562,11 +581,17 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
splitPane1.setRightComponent(panel2);
final JLabel label2 = new JLabel();
Font label2Font = this.$$$getFont$$$(null, Font.BOLD, 16, label2.getFont());
- if (label2Font != null) label2.setFont(label2Font);
+ if (label2Font != null)
+ label2.setFont(label2Font);
label2.setText("Monitored directories");
- panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane2 = new JScrollPane();
- panel2.add(scrollPane2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ panel2.add(scrollPane2,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null,
+ 0, false));
monitoredDirectoryList = new JList();
monitoredDirectoryList.setEnabled(true);
monitoredDirectoryList.setSelectionMode(0);
@@ -574,56 +599,96 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
scrollPane2.setViewportView(monitoredDirectoryList);
notificationPanel = new JPanel();
notificationPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 10, 0), -1, -1));
- homeTab.add(notificationPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
+ homeTab.add(notificationPanel,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
+ 0, true));
final JLabel label3 = new JLabel();
Font label3Font = this.$$$getFont$$$(null, Font.BOLD, 16, label3.getFont());
- if (label3Font != null) label3.setFont(label3Font);
+ if (label3Font != null)
+ label3.setFont(label3Font);
label3.setText("Notifications");
- notificationPanel.add(label3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ notificationPanel.add(label3,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
notificationTextArea = new JTextArea();
notificationTextArea.setEditable(false);
- notificationPanel.add(notificationTextArea, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, new Dimension(150, 50), null, 0, false));
+ notificationPanel.add(notificationTextArea,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null,
+ new Dimension(150, 50), null, 0, false));
scanTab = new JPanel();
scanTab.setLayout(new GridLayoutManager(10, 4, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("\uD83D\uDD0E Scan", scanTab);
final JLabel label4 = new JLabel();
Font label4Font = this.$$$getFont$$$(null, Font.BOLD, 16, label4.getFont());
- if (label4Font != null) label4.setFont(label4Font);
+ if (label4Font != null)
+ label4.setFont(label4Font);
label4.setHorizontalAlignment(0);
label4.setText("Monitored directories");
- scanTab.add(label4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(label4,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane3 = new JScrollPane();
scrollPane3.setToolTipText("Right click a monitored directory for different actions");
- scanTab.add(scrollPane3, new GridConstraints(1, 0, 5, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ scanTab.add(scrollPane3,
+ new GridConstraints(1, 0, 5, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null,
+ 0, false));
monitoredDirectoriesTable = new JTable();
monitoredDirectoriesTable.setAutoResizeMode(2);
scrollPane3.setViewportView(monitoredDirectoriesTable);
liveFeedContainer = new JScrollPane();
- scanTab.add(liveFeedContainer, new GridConstraints(1, 1, 8, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(450, -1), new Dimension(450, -1), null, 0, false));
+ scanTab.add(liveFeedContainer,
+ new GridConstraints(1, 1, 8, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ new Dimension(450, -1), new Dimension(450, -1), null, 0, false));
liveFeedText = new JTextPane();
liveFeedText.setEditable(false);
liveFeedText.setText("");
liveFeedContainer.setViewportView(liveFeedText);
liveFeedTitle = new JLabel();
Font liveFeedTitleFont = this.$$$getFont$$$(null, Font.BOLD, 16, liveFeedTitle.getFont());
- if (liveFeedTitleFont != null) liveFeedTitle.setFont(liveFeedTitleFont);
+ if (liveFeedTitleFont != null)
+ liveFeedTitle.setFont(liveFeedTitleFont);
liveFeedTitle.setHorizontalAlignment(0);
liveFeedTitle.setText("Scan logs");
- scanTab.add(liveFeedTitle, new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(liveFeedTitle,
+ new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
liveFeedDiffCount = new JLabel();
liveFeedDiffCount.setText("");
- scanTab.add(liveFeedDiffCount, new GridConstraints(9, 2, 1, 2, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(liveFeedDiffCount,
+ new GridConstraints(9, 2, 1, 2, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
scanButton = new JButton();
scanButton.setText("Full scan");
- scanTab.add(scanButton, new GridConstraints(6, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(scanButton,
+ new GridConstraints(6, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
clearFeedButton = new JButton();
clearFeedButton.setText("Clear feed");
- scanTab.add(clearFeedButton, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(clearFeedButton,
+ new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
filesTab = new JPanel();
filesTab.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("\uD83D\uDDC4\uFE0F Files", filesTab);
final JSplitPane splitPane2 = new JSplitPane();
- filesTab.add(splitPane2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
+ filesTab.add(splitPane2,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
+ new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane4 = new JScrollPane();
splitPane2.setLeftComponent(scrollPane4);
filesTable = new JTable();
@@ -633,7 +698,11 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
splitPane2.setRightComponent(panel3);
final JSplitPane splitPane3 = new JSplitPane();
splitPane3.setOrientation(0);
- panel3.add(splitPane3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
+ panel3.add(splitPane3,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
+ new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane5 = new JScrollPane();
splitPane3.setLeftComponent(scrollPane5);
fileSummaryTable = new JTable();
@@ -645,26 +714,47 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
scrollPane6.setViewportView(scanSummaryDetails);
final JPanel panel4 = new JPanel();
panel4.setLayout(new GridLayoutManager(5, 2, new Insets(0, 0, 0, 0), -1, -1));
- filesTab.add(panel4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
+ filesTab.add(panel4,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
+ 0, true));
fileSearchField = new JTextField();
- panel4.add(fileSearchField, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
+ panel4.add(fileSearchField,
+ new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null,
+ new Dimension(150, -1), null, 0, false));
final JLabel label5 = new JLabel();
label5.setText("Search for file");
- panel4.add(label5, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(label5, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
searchContainingCheckBox = new JCheckBox();
searchContainingCheckBox.setText("File name contains");
- panel4.add(searchContainingCheckBox, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(searchContainingCheckBox,
+ new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
descendingCheckBox = new JCheckBox();
descendingCheckBox.setText("Descending");
- panel4.add(descendingCheckBox, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(descendingCheckBox,
+ new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
searchResultCount = new JLabel();
searchResultCount.setText("");
- panel4.add(searchResultCount, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(searchResultCount,
+ new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
recentDiffsTab = new JPanel();
recentDiffsTab.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("⚠\uFE0F Recent diffs", recentDiffsTab);
final JSplitPane splitPane4 = new JSplitPane();
- recentDiffsTab.add(splitPane4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
+ recentDiffsTab.add(splitPane4,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
+ new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane7 = new JScrollPane();
splitPane4.setLeftComponent(scrollPane7);
diffTable = new JTable();
@@ -677,38 +767,63 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
scrollPane8.setViewportView(diffDetails);
final JLabel label6 = new JLabel();
Font label6Font = this.$$$getFont$$$(null, Font.BOLD, 16, label6.getFont());
- if (label6Font != null) label6.setFont(label6Font);
+ if (label6Font != null)
+ label6.setFont(label6Font);
label6.setText("Most recently detected diffs");
- recentDiffsTab.add(label6, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ recentDiffsTab.add(label6,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
settingsTab = new JPanel();
settingsTab.setLayout(new GridLayoutManager(4, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("⚙\uFE0F Settings", settingsTab);
final JSplitPane splitPane5 = new JSplitPane();
- settingsTab.add(splitPane5, new GridConstraints(0, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
+ settingsTab.add(splitPane5,
+ new GridConstraints(0, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
+ new Dimension(200, 200), null, 0, false));
final JPanel panel5 = new JPanel();
panel5.setLayout(new GridLayoutManager(7, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane5.setLeftComponent(panel5);
final JLabel label7 = new JLabel();
Font label7Font = this.$$$getFont$$$(null, Font.BOLD, 14, label7.getFont());
- if (label7Font != null) label7.setFont(label7Font);
+ if (label7Font != null)
+ label7.setFont(label7Font);
label7.setText("Settings");
- panel5.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final Spacer spacer1 = new Spacer();
- panel5.add(spacer1, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ panel5.add(spacer1, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER,
+ GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
themePicker = new JComboBox();
- panel5.add(themePicker, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(themePicker,
+ new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
restartButton = new JButton();
restartButton.setText("Restart");
- panel5.add(restartButton, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(restartButton,
+ new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label8 = new JLabel();
label8.setText("© 2025 PWSS ORG");
- panel5.add(label8, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(label8, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel6 = new JPanel();
panel6.setLayout(new GridLayoutManager(6, 1, new Insets(0, 0, 0, 0), -1, -1));
- panel5.add(panel6, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ panel5.add(panel6,
+ new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
+ 0, false));
showSplashScreenCheckBox = new JCheckBox();
showSplashScreenCheckBox.setText("Show splash screen");
- panel6.add(showSplashScreenCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(showSplashScreenCheckBox,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
maxHashExtractionFileSizeSlider = new JSlider();
maxHashExtractionFileSizeSlider.setMajorTickSpacing(5120);
maxHashExtractionFileSizeSlider.setMaximum(102400);
@@ -718,49 +833,80 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
maxHashExtractionFileSizeSlider.setPaintTicks(true);
maxHashExtractionFileSizeSlider.setPaintTrack(true);
maxHashExtractionFileSizeSlider.setSnapToTicks(true);
- panel6.add(maxHashExtractionFileSizeSlider, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(maxHashExtractionFileSizeSlider,
+ new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
final JLabel label9 = new JLabel();
- label9.setText("Max hash exctraction file size (MB)");
- panel6.add(label9, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ label9.setText("Max hash extraction file size (MB)");
+ panel6.add(label9, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JSeparator separator1 = new JSeparator();
- panel6.add(separator1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ panel6.add(separator1,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0,
+ false));
maxHashExtractionFileSizeValueLabel = new JLabel();
maxHashExtractionFileSizeValueLabel.setText("Selected:");
- panel6.add(maxHashExtractionFileSizeValueLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(maxHashExtractionFileSizeValueLabel,
+ new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
maxHashExtractionFileSizeUnlimitedCheckbox = new JCheckBox();
maxHashExtractionFileSizeUnlimitedCheckbox.setText("Unlimited");
- panel6.add(maxHashExtractionFileSizeUnlimitedCheckbox, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(maxHashExtractionFileSizeUnlimitedCheckbox,
+ new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label10 = new JLabel();
label10.setText("Theme");
- panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel7 = new JPanel();
panel7.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane5.setRightComponent(panel7);
final JScrollPane scrollPane9 = new JScrollPane();
- panel7.add(scrollPane9, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ panel7.add(scrollPane9,
+ new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
+ 0, false));
quarantineTable = new JTable();
scrollPane9.setViewportView(quarantineTable);
final JLabel label11 = new JLabel();
Font label11Font = this.$$$getFont$$$(null, Font.BOLD, 14, label11.getFont());
- if (label11Font != null) label11.setFont(label11Font);
+ if (label11Font != null)
+ label11.setFont(label11Font);
label11.setText("Quarantined files");
- panel7.add(label11, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel7.add(label11, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
scanProgressContainer = new JPanel();
scanProgressContainer.setLayout(new GridLayoutManager(1, 2, new Insets(10, 10, 10, 10), -1, -1));
- rootPanel.add(scanProgressContainer, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
+ rootPanel.add(scanProgressContainer,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_FIXED,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
+ 0, true));
scanProgress = new JProgressBar();
scanProgress.setIndeterminate(true);
- scanProgressContainer.add(scanProgress, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanProgressContainer.add(scanProgress,
+ new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
scanProgressLabel = new JLabel();
scanProgressLabel.setText("Scan in progress");
- scanProgressContainer.add(scanProgressLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanProgressContainer.add(scanProgressLabel,
+ new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
+ false));
}
/**
* @noinspection ALL
*/
private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
- if (currentFont == null) return null;
+ if (currentFont == null)
+ return null;
String resultName;
if (fontName == null) {
resultName = currentFont.getName();
@@ -772,9 +918,11 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
resultName = currentFont.getName();
}
}
- Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
+ Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(),
+ size >= 0 ? size : currentFont.getSize());
boolean isMac = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).startsWith("mac");
- Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize()) : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize());
+ Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize())
+ : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize());
return fontWithFallback instanceof FontUIResource ? fontWithFallback : new FontUIResource(fontWithFallback);
}
From ad4b9940d332539ecb9e4ed0704e08c4c1c810d0 Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Sun, 2 Nov 2025 22:09:35 +0100
Subject: [PATCH 16/44] Updated the Start Script & set log level to INFO
---
src/main/resources/logback.xml | 2 +-
start.ps1 | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index bf24e35..83566ff 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -11,7 +11,7 @@
-
+
\ No newline at end of file
diff --git a/start.ps1 b/start.ps1
index c6d4600..d389128 100644
--- a/start.ps1
+++ b/start.ps1
@@ -8,14 +8,14 @@ if (-not (Test-Path -Path "..\artifacts")) {
if ($null -eq $portInUse) {
Write-Host "Nothing is running on port 15400. Starting the process..."
- java -jar "..\File-Integrity-Scanner\File-Integrity-Scanner\target\File-Integrity-Scanner-1.5.1.jar" &
+ java -jar "..\File-Integrity-Scanner\File-Integrity-Scanner\target\File-Integrity-Scanner-1.7.jar" &
Write-Host "File-Integrity-Scanner started."
- cp .\target\integrity_hash-1.0-jar-with-dependencies.jar ..\artifacts
- java -jar ..\artifacts\integrity_hash-1.0-jar-with-dependencies.jar
+ cp .\target\integrity_hash-1.1-jar-with-dependencies.jar ..\artifacts
+ java -jar ..\artifacts\integrity_hash-1.1-jar-with-dependencies.jar
exit
} else {
Write-Host "File-Integrity-Scanner is already running on port 15400."
- cp .\target\integrity_hash-1.0-jar-with-dependencies.jar ..\artifacts
- java -jar ..\artifacts\integrity_hash-1.0-jar-with-dependencies.jar
+ cp .\target\integrity_hash-1.1-jar-with-dependencies.jar ..\artifacts
+ java -jar ..\artifacts\integrity_hash-1.1-jar-with-dependencies.jar
exit
}
From 0c52dcecffbd06b4b15102d8f20ee5cf3779d895 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Mon, 3 Nov 2025 18:45:44 +0100
Subject: [PATCH 17/44] Implement password complexity validation when creating
user and added unit tests for validator :)
---
.../org/pwss/controller/LoginController.java | 23 ++-----
src/main/java/org/pwss/utils/LoginUtils.java | 49 +++++++++++++
.../java/org/pwss/utils/LoginUtilsTest.java | 68 +++++++++++++++++++
3 files changed, 124 insertions(+), 16 deletions(-)
create mode 100644 src/main/java/org/pwss/utils/LoginUtils.java
create mode 100644 src/test/java/org/pwss/utils/LoginUtilsTest.java
diff --git a/src/main/java/org/pwss/controller/LoginController.java b/src/main/java/org/pwss/controller/LoginController.java
index 7e3a4a4..ec5f3bb 100644
--- a/src/main/java/org/pwss/controller/LoginController.java
+++ b/src/main/java/org/pwss/controller/LoginController.java
@@ -10,6 +10,7 @@
import org.pwss.navigation.NavigationEvents;
import org.pwss.navigation.Screen;
import org.pwss.service.AuthService;
+import org.pwss.utils.LoginUtils;
import org.pwss.view.screen.LoginScreen;
import org.slf4j.LoggerFactory;
@@ -144,31 +145,21 @@ private void proceedAndValidate() {
}
/**
- * Validates the input fields for username and password.
+ * Validates the user input for username, password, and license key.
*
- * @return true if both fields are non-empty, false otherwise.
+ * @return true if input is valid, false otherwise.
*/
private boolean validateInput() {
String username = getScreen().getUsername();
String password = getScreen().getPassword();
String licenseKey = licenseKeySet ? LICENSE_KEY : getScreen().getLicenseKey();
- if (username == null || username.trim().isEmpty()) {
- getScreen().showError("Username cannot be empty.");
- return false;
- }
-
- if (password == null || password.trim().isEmpty()) {
- getScreen().showError("Password cannot be empty.");
- return false;
- }
-
- if (licenseKey == null || licenseKey.trim().isEmpty()) {
- getScreen().showError("License Key cannot be empty.");
- return false;
+ LoginUtils.LoginValidationResult result = LoginUtils.validateInput(username, password, licenseKey, createUserMode);
+ if (!result.isValid()) {
+ getScreen().showError(result.errorMessage());
}
- return true;
+ return result.isValid();
}
/**
diff --git a/src/main/java/org/pwss/utils/LoginUtils.java b/src/main/java/org/pwss/utils/LoginUtils.java
new file mode 100644
index 0000000..ce879eb
--- /dev/null
+++ b/src/main/java/org/pwss/utils/LoginUtils.java
@@ -0,0 +1,49 @@
+package org.pwss.utils;
+
+/**
+ * Utility class for validating login inputs.
+ */
+public final class LoginUtils {
+
+ /**
+ * Result of login validation.
+ *
+ * @param isValid True if the input is valid, false otherwise.
+ * @param errorMessage The error message if invalid, null if valid.
+ */
+ public record LoginValidationResult(boolean isValid, String errorMessage) {}
+
+ /**
+ * Validates the user input for username, password, and license key.
+ *
+ * @param username The username input.
+ * @param password The password input.
+ * @param licenseKey The license key input.
+ * @param createUserMode True if in user creation mode, false otherwise.
+ * @return A LoginValidationResult indicating whether the input is valid and any error message.
+ */
+ public static LoginValidationResult validateInput(String username, String password, String licenseKey, boolean createUserMode) {
+ if (username == null || username.trim().isEmpty()) {
+ return new LoginValidationResult(false, "Username cannot be empty.");
+ }
+ if (password == null || password.trim().isEmpty()) {
+ return new LoginValidationResult(false, "Password cannot be empty.");
+ }
+ if (createUserMode && password.length() < 8) {
+ return new LoginValidationResult(false, "Password must be at least 8 characters long.");
+ }
+ if (createUserMode && !password.matches(".*[A-Z].*")) {
+ return new LoginValidationResult(false, "Password must contain at least one uppercase letter.");
+ }
+ if (createUserMode && !password.matches(".*\\d.*")) {
+ return new LoginValidationResult(false, "Password must contain at least one digit.");
+ }
+ if (createUserMode && !password.matches(".*[!@#$%^&*(),.?\":{}|<>].*")) {
+ return new LoginValidationResult(false, "Password must contain at least one special character.");
+ }
+ if (licenseKey == null || licenseKey.trim().isEmpty()) {
+ return new LoginValidationResult(false, "License key cannot be empty.");
+ }
+ return new LoginValidationResult(true, null);
+ }
+}
diff --git a/src/test/java/org/pwss/utils/LoginUtilsTest.java b/src/test/java/org/pwss/utils/LoginUtilsTest.java
new file mode 100644
index 0000000..ad19b2a
--- /dev/null
+++ b/src/test/java/org/pwss/utils/LoginUtilsTest.java
@@ -0,0 +1,68 @@
+package org.pwss.utils;
+
+import org.junit.jupiter.api.Test;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class LoginUtilsTest {
+
+ @Test
+ void validateInput_returnsValidResult_whenAllInputsAreValid() {
+ var result = LoginUtils.validateInput("validUser", "Valid123!", "LICENSE123", false);
+ assertTrue(result.isValid());
+ assertNull(result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenUsernameIsEmpty() {
+ var result = LoginUtils.validateInput("", "Valid123!", "LICENSE123", false);
+ assertFalse(result.isValid());
+ assertEquals("Username cannot be empty.", result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenPasswordIsEmpty() {
+ var result = LoginUtils.validateInput("validUser", "", "LICENSE123", false);
+ assertFalse(result.isValid());
+ assertEquals("Password cannot be empty.", result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenPasswordTooShortInCreateUserMode() {
+ var result = LoginUtils.validateInput("validUser", "Short1!", "LICENSE123", true);
+ assertFalse(result.isValid());
+ assertEquals("Password must be at least 8 characters long.", result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenPasswordMissingUppercaseInCreateUserMode() {
+ var result = LoginUtils.validateInput("validUser", "valid123!", "LICENSE123", true);
+ assertFalse(result.isValid());
+ assertEquals("Password must contain at least one uppercase letter.", result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenPasswordMissingDigitInCreateUserMode() {
+ var result = LoginUtils.validateInput("validUser", "ValidPass!", "LICENSE123", true);
+ assertFalse(result.isValid());
+ assertEquals("Password must contain at least one digit.", result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenPasswordMissingSpecialCharacterInCreateUserMode() {
+ var result = LoginUtils.validateInput("validUser", "Valid1234", "LICENSE123", true);
+ assertFalse(result.isValid());
+ assertEquals("Password must contain at least one special character.", result.errorMessage());
+ }
+
+ @Test
+ void validateInput_returnsError_whenLicenseKeyIsEmpty() {
+ var result = LoginUtils.validateInput("validUser", "Valid123!", "", false);
+ assertFalse(result.isValid());
+ assertEquals("License key cannot be empty.", result.errorMessage());
+ }
+}
From 477a4d8402ad3fa6cb04465deb5c7040670e3fe0 Mon Sep 17 00:00:00 2001
From: Stefan <130162586+lilstiffy@users.noreply.github.com>
Date: Mon, 3 Nov 2025 18:50:27 +0100
Subject: [PATCH 18/44] Update src/main/java/org/pwss/utils/LoginUtils.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/main/java/org/pwss/utils/LoginUtils.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/main/java/org/pwss/utils/LoginUtils.java b/src/main/java/org/pwss/utils/LoginUtils.java
index ce879eb..e787c96 100644
--- a/src/main/java/org/pwss/utils/LoginUtils.java
+++ b/src/main/java/org/pwss/utils/LoginUtils.java
@@ -5,6 +5,10 @@
*/
public final class LoginUtils {
+ // Private constructor to prevent instantiation
+ private LoginUtils() {
+ // This constructor is intentionally empty.
+ }
/**
* Result of login validation.
*
From 989ea50eeb1a8b8a005f8986b263833030dbb0a3 Mon Sep 17 00:00:00 2001
From: Stefan <130162586+lilstiffy@users.noreply.github.com>
Date: Mon, 3 Nov 2025 18:50:49 +0100
Subject: [PATCH 19/44] Update src/main/java/org/pwss/utils/LoginUtils.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/main/java/org/pwss/utils/LoginUtils.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/utils/LoginUtils.java b/src/main/java/org/pwss/utils/LoginUtils.java
index e787c96..7bc0539 100644
--- a/src/main/java/org/pwss/utils/LoginUtils.java
+++ b/src/main/java/org/pwss/utils/LoginUtils.java
@@ -33,7 +33,7 @@ public static LoginValidationResult validateInput(String username, String passwo
if (password == null || password.trim().isEmpty()) {
return new LoginValidationResult(false, "Password cannot be empty.");
}
- if (createUserMode && password.length() < 8) {
+ if (createUserMode && password.trim().length() < 8) {
return new LoginValidationResult(false, "Password must be at least 8 characters long.");
}
if (createUserMode && !password.matches(".*[A-Z].*")) {
From 32755bfbc672a6ac2ebd3017cfbd7b50aa79cbf8 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Mon, 3 Nov 2025 19:16:47 +0100
Subject: [PATCH 20/44] Editable includeSubDirs & active status for a monitored
directory
---
.../service/MonitoredDirectoryService.java | 74 +++++++++++++------
.../java/org/pwss/utils/StringConstants.java | 9 ++-
.../MonitoredDirectoryPopupFactory.java | 32 ++++++--
.../MonitoredDirectoryPopupListener.java | 13 +++-
.../MonitoredDirectoryPopupListenerImpl.java | 29 +++++++-
5 files changed, 120 insertions(+), 37 deletions(-)
diff --git a/src/main/java/org/pwss/service/MonitoredDirectoryService.java b/src/main/java/org/pwss/service/MonitoredDirectoryService.java
index 8477cf2..8e3b9bb 100644
--- a/src/main/java/org/pwss/service/MonitoredDirectoryService.java
+++ b/src/main/java/org/pwss/service/MonitoredDirectoryService.java
@@ -153,47 +153,77 @@ public MonitoredDirectory createNewMonitoredDirectory(String path, boolean inclu
}
/**
- * Updates an existing monitored directory by sending a request to the
- * MONITORED_DIRECTORY_UPDATE endpoint.
+ * Toggles the active status of a monitored directory by sending a request to
+ * the MONITORED_DIRECTORY_UPDATE endpoint.
*
- * @param id The ID of the monitored directory to update.
- * @param isActive The new active status of the monitored directory.
- * @param notes Any notes associated with the monitored directory.
- * @param includeSubDirs Whether to include subdirectories in monitoring.
+ * @param dir The MonitoredDirectory object representing the directory to be
+ * updated.
* @return `true` if the update was successful, otherwise false.
* @throws UpdateMonitoredDirectoryException If the update attempt fails due to
- * invalid input, unauthorized access,
- * or server error.
+ * invalid input, unauthorized access,
+ * or server error.
* @throws ExecutionException If an error occurs during the
- * asynchronous execution of the
- * request.
+ * asynchronous execution of the request.
* @throws InterruptedException If the thread executing the request
- * is interrupted.
+ * is interrupted.
* @throws JsonProcessingException If an error occurs while
- * serializing the request body.
+ * serializing the request body.
*/
- public boolean updateMonitoredDirectory(long id, boolean isActive, String notes, boolean includeSubDirs)
- throws UpdateMonitoredDirectoryException, JsonProcessingException, ExecutionException,
- InterruptedException {
+ public boolean toggleActive(MonitoredDirectory dir) throws UpdateMonitoredDirectoryException, JsonProcessingException, ExecutionException, InterruptedException {
String body = objectMapper
- .writeValueAsString(new UpdateDirectoryRequest(id, isActive, notes, includeSubDirs));
+ .writeValueAsString(new UpdateDirectoryRequest(dir.id(), !dir.isActive(), dir.notes().notes(), dir.includeSubdirectories()));
HttpResponse response = PwssHttpClient.getInstance()
.request(Endpoint.MONITORED_DIRECTORY_UPDATE, body);
return switch (response.statusCode()) {
case 200 -> true;
- case 400 -> throw new UpdateMonitoredDirectoryException(
- "Update monitored directory failed: invalid input data.");
+ case 400 ->
+ throw new UpdateMonitoredDirectoryException("Update monitored directory failed: invalid input data.");
+ case 401 ->
+ throw new UpdateMonitoredDirectoryException("Update monitored directory failed: User not authorized to perform this action.");
+ case 500 ->
+ throw new UpdateMonitoredDirectoryException("Update monitored directory failed: An error occurred on the server while attempting to update the monitored directory.");
+ default -> false;
+ };
+ }
+
+ /**
+ * Toggles the inclusion of subdirectories for a monitored directory by sending
+ * a request to the MONITORED_DIRECTORY_UPDATE endpoint.
+ *
+ * @param dir The MonitoredDirectory object representing the directory to be
+ * updated.
+ * @return `true` if the update was successful, otherwise false.
+ * @throws UpdateMonitoredDirectoryException If the update attempt fails due to
+ * invalid input, unauthorized access,
+ * or server error.
+ * @throws ExecutionException If an error occurs during the
+ * asynchronous execution of the request.
+ * @throws InterruptedException If the thread executing the request
+ * is interrupted.
+ * @throws JsonProcessingException If an error occurs while
+ * serializing the request body.
+ */
+ public boolean toggleIncludeSubDirectories(MonitoredDirectory dir) throws UpdateMonitoredDirectoryException, JsonProcessingException, ExecutionException, InterruptedException {
+ String body = objectMapper
+ .writeValueAsString(new UpdateDirectoryRequest(dir.id(), dir.isActive(), dir.notes().notes(), !dir.includeSubdirectories()));
+ HttpResponse response = PwssHttpClient.getInstance()
+ .request(Endpoint.MONITORED_DIRECTORY_UPDATE, body);
+
+ return switch (response.statusCode()) {
+ case 200 -> true;
+ case 400 ->
+ throw new UpdateMonitoredDirectoryException("Update monitored directory failed: invalid input data.");
case 401 ->
- throw new UpdateMonitoredDirectoryException(
- "Update monitored directory failed: User not authorized to perform this action.");
+ throw new UpdateMonitoredDirectoryException("Update monitored directory failed: User not authorized to perform this action.");
case 500 ->
- throw new UpdateMonitoredDirectoryException(
- "Update monitored directory failed: An error occurred on the server while attempting to update the monitored directory.");
+ throw new UpdateMonitoredDirectoryException("Update monitored directory failed: An error occurred on the server while attempting to update the monitored directory.");
default -> false;
};
}
+
+
/**
* Creates a new baseline for a monitored directory by sending a request to the
* MONITORED_DIRECTORY_NEW_BASELINE endpoint.
diff --git a/src/main/java/org/pwss/utils/StringConstants.java b/src/main/java/org/pwss/utils/StringConstants.java
index 29526f1..25c68ac 100644
--- a/src/main/java/org/pwss/utils/StringConstants.java
+++ b/src/main/java/org/pwss/utils/StringConstants.java
@@ -48,7 +48,14 @@ public final class StringConstants {
public static final String MON_DIR_POPUP_RESET_BASELINE_ERROR = "Failed to reset baseline.";
public static final String MON_DIR_POPUP_RESET_BASELINE_ERROR_INVALID = "Invalid code. Baseline reset cancelled.";
public static final String MON_DIR_POPUP_RESET_BASELINE_ERROR_PREFIX = "Error resetting baseline: ";
- public static final String MON_DIR_POPUP_EDIT_DIR = "Edit this directory";
+ public static final String MON_DIR_TOGGLE_ACTIVE_ENABLE = "Set directory as active";
+ public static final String MON_DIR_TOGGLE_ACTIVE_DISABLE = "Set directory as inactive";
+ public static final String MON_DIR_TOGGLE_ACTIVE_SUCCESS = "Directory status updated successfully.";
+ public static final String MON_DIR_TOGGLE_ACTIVE_ERROR = "Failed to update directory status.";
+ public static final String MON_DIR_TOGGLE_INCLUDE_SUBDIR_ENABLE = "Include subdirectories";
+ public static final String MON_DIR_TOGGLE_INCLUDE_SUBDIR_DISABLE = "Exclude subdirectories";
+ public static final String MON_DIR_TOGGLE_INCLUDE_SUBDIR_SUCCESS = "Subdirectory inclusion status updated successfully.";
+ public static final String MON_DIR_TOGGLE_INCLUDE_SUBDIR_ERROR = "Failed to update subdirectory inclusion status.";
// Show note related strings
public static final String MON_DIR_POPUP_SHOW_NOTE = "Show note";
diff --git a/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java b/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
index ade03a6..99c0797 100644
--- a/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
+++ b/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
@@ -49,8 +49,11 @@ public JPopupMenu create(JTable table, int viewRow) {
// Reset baseline
JMenuItem resetBaselineItem = getResetBaselineItem(dir);
- // Edit directory
- JMenuItem editDirectory = getEditDirectoryItem(dir);
+ // Toggle active state
+ JMenuItem toggleActiveMenuItem = getToggleActiveMenuItem(dir);
+
+ // Toggle include subdirectories
+ JMenuItem toggleIncludeSubDirsItem = getToggleIncludeSubDirsItem(dir);
// Show note
JMenuItem showNoteItem = getShowNoteItem(dir);
@@ -63,7 +66,8 @@ public JPopupMenu create(JTable table, int viewRow) {
// Assemble menu
menu.add(scanItem);
- menu.add(editDirectory);
+ menu.add(toggleActiveMenuItem);
+ menu.add(toggleIncludeSubDirsItem);
menu.addSeparator();
menu.add(showNoteItem);
menu.add(updateNoteItem);
@@ -75,14 +79,26 @@ public JPopupMenu create(JTable table, int viewRow) {
}
/**
- * Creates a menu item for editing the details of a monitored directory.
+ * Creates a menu item for toggling the active status of a monitored directory.
+ *
+ * @param dir the monitored directory for which to create the menu item
+ * @return the JMenuItem for toggling the active status
+ */
+ private JMenuItem getToggleActiveMenuItem(MonitoredDirectory dir) {
+ JMenuItem editItem = new JMenuItem(dir.isActive() ? StringConstants.MON_DIR_TOGGLE_ACTIVE_DISABLE : StringConstants.MON_DIR_TOGGLE_ACTIVE_ENABLE);
+ editItem.addActionListener(e -> listener.onToggleActiveStatus(dir));
+ return editItem;
+ }
+
+ /**
+ * Creates a menu item for toggling the inclusion of subdirectories in a monitored directory.
*
* @param dir the monitored directory for which to create the menu item
- * @return the JMenuItem for editing the directory
+ * @return the JMenuItem for toggling the inclusion of subdirectories
*/
- private JMenuItem getEditDirectoryItem(MonitoredDirectory dir) {
- JMenuItem editItem = new JMenuItem(StringConstants.MON_DIR_POPUP_EDIT_DIR);
- editItem.addActionListener(e -> listener.onEditDirectory(dir));
+ private JMenuItem getToggleIncludeSubDirsItem(MonitoredDirectory dir) {
+ JMenuItem editItem = new JMenuItem(dir.includeSubdirectories() ? StringConstants.MON_DIR_TOGGLE_INCLUDE_SUBDIR_DISABLE : StringConstants.MON_DIR_TOGGLE_INCLUDE_SUBDIR_ENABLE);
+ editItem.addActionListener(e -> listener.onToggleIncludeSubdirectories(dir));
return editItem;
}
diff --git a/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListener.java b/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListener.java
index 9cc465a..f81ded5 100644
--- a/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListener.java
+++ b/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListener.java
@@ -23,11 +23,18 @@ public interface MonitoredDirectoryPopupListener {
void onResetBaseline(MonitoredDirectory dir, long endpointCode);
/**
- * Triggered when a monitored directory is edited.
+ * Triggered when the active status of a monitored directory is toggled.
*
- * @param dir The monitored directory to be edited.
+ * @param dir The monitored directory whose active status is being toggled.
*/
- void onEditDirectory(MonitoredDirectory dir);
+ void onToggleActiveStatus(MonitoredDirectory dir);
+
+ /**
+ * Triggered when the inclusion of subdirectories for a monitored directory is toggled.
+ *
+ * @param dir The monitored directory whose subdirectory inclusion is being toggled.
+ */
+ void onToggleIncludeSubdirectories(MonitoredDirectory dir);
/**
* Triggered when the notes for a monitored directory are updated.
diff --git a/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java b/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java
index 85b1398..1080fab 100644
--- a/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java
+++ b/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java
@@ -68,10 +68,33 @@ public void onResetBaseline(MonitoredDirectory dir, long endpointCode) {
}
@Override
- public void onEditDirectory(MonitoredDirectory dir) {
- // TODO: Implement edit directory functionality
+ public void onToggleActiveStatus(MonitoredDirectory dir) {
+ try {
+ if (directoryService.toggleActive(dir)) {
+ showSuccess(StringConstants.MON_DIR_TOGGLE_ACTIVE_SUCCESS);
+ controller.reloadData();
+ } else {
+ showError(StringConstants.MON_DIR_TOGGLE_ACTIVE_ERROR);
+ }
+ } catch (Exception e) {
+ log.error("Error toggling active status for directory {}: {}", dir.path(), e.getMessage());
+ showError(e.getMessage());
+ }
+ }
- // We need to be able to update and save notes. Maybe here? :) / Pwgit-Create
+ @Override
+ public void onToggleIncludeSubdirectories(MonitoredDirectory dir) {
+ try {
+ if (directoryService.toggleIncludeSubDirectories(dir)) {
+ showSuccess(StringConstants.MON_DIR_TOGGLE_INCLUDE_SUBDIR_SUCCESS);
+ controller.reloadData();
+ } else {
+ showError(StringConstants.MON_DIR_TOGGLE_INCLUDE_SUBDIR_ERROR);
+ }
+ } catch (Exception e) {
+ log.error("Error toggling include subdirectories for directory {}: {}", dir.path(), e.getMessage());
+ showError(e.getMessage());
+ }
}
@Override
From 59d53bfdc8fee8e4e267f12998e9e5f9d6e573bd Mon Sep 17 00:00:00 2001
From: Stefan
Date: Mon, 3 Nov 2025 19:50:39 +0100
Subject: [PATCH 21/44] Fix things that came up from discussion with
@pwgit-create
---
.../org/pwss/model/table/MonitoredDirectoryTableModel.java | 5 +++--
.../pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java | 4 +++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java b/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java
index cc05bd2..4c3edc8 100644
--- a/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java
+++ b/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java
@@ -9,7 +9,7 @@
public class MonitoredDirectoryTableModel extends AbstractTableModel {
private final List directories;
private final String[] columnNames = {
- "\uD83D\uDCC1 Path", "\uD83D\uDEA6 Note", "\uD83D\uDD59 Last Scanned", "\uD83D\uDEE1️ Baseline Established", "\uD83D\uDDC2️ Include Subdirectories"
+ "\uD83D\uDCC1 Path", "\uD83D\uDEA6 Note", "\uD83D\uDD59 Last Scanned", "\uD83D\uDEE1️ Baseline Established", "\uD83D\uDDC2️ Include Subdirectories", "Active"
};
public MonitoredDirectoryTableModel(List directories) {
@@ -35,7 +35,7 @@ public String getColumnName(int column) {
public Class> getColumnClass(int columnIndex) {
return switch (columnIndex) {
case 2 -> Date.class;
- case 4 -> Boolean.class;
+ case 4, 5 -> Boolean.class;
default -> super.getColumnClass(columnIndex);
};
}
@@ -49,6 +49,7 @@ public Object getValueAt(int rowIndex, int columnIndex) {
case 2 -> dir.lastScanned();
case 3 -> dir.baselineEstablished() ? "Yes" : "No";
case 4 -> dir.includeSubdirectories();
+ case 5 -> dir.isActive();
default -> null;
};
}
diff --git a/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java b/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
index 99c0797..8447121 100644
--- a/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
+++ b/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
@@ -65,7 +65,9 @@ public JPopupMenu create(JTable table, int viewRow) {
JMenuItem restoreNoteItem = getRestoreNoteItem(dir);
// Assemble menu
- menu.add(scanItem);
+ if (dir.isActive()) {
+ menu.add(scanItem);
+ }
menu.add(toggleActiveMenuItem);
menu.add(toggleIncludeSubDirsItem);
menu.addSeparator();
From 5d21423f89fcab12325f010a16fb80d6dcc1461f Mon Sep 17 00:00:00 2001
From: Stefan
Date: Mon, 3 Nov 2025 20:41:13 +0100
Subject: [PATCH 22/44] Fix unwanted error message caused when having no active
monitored directories and trying to fetch their scans
---
src/main/java/org/pwss/controller/HomeController.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 645209c..9315817 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -250,8 +250,9 @@ void fetchDataAndRefreshView() {
// table
allMonitoredDirectories = monitoredDirectoryService.getAllDirectories();
- // Only fetch diffs if there are monitored directories present
- if (!allMonitoredDirectories.isEmpty()) {
+ // Only fetch diffs if there are active monitored directories present
+ final long activeDirCount = allMonitoredDirectories.stream().filter(MonitoredDirectory::isActive).count();
+ if (activeDirCount > 0) {
// Fetch recent scans for display in the scan table
recentScans = scanService.getMostRecentScansAll();
if (recentScans.isEmpty()) {
From 57f81ce4b6379c43e760dbd5a60fb73f4c2b15e9 Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Mon, 3 Nov 2025 22:59:01 +0100
Subject: [PATCH 23/44] =?UTF-8?q?=E2=9C=A8=20Update=20emojis,=20add=20Erro?=
=?UTF-8?q?r=20Utils=20class,=20and=20improve=20error=20dialog?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- New "Active" emoji (🔌 plug-in symbol)
- Introduced `ErrorUtils` class to handle and format error messages consistently
- "Note" emoji: traffic light → page & pen 📝
- "Baseline Established" emoji: previous unclear emoji → anchor ⚓
- "Include Subdirs" emoji: snowboarder 🏂 → open folder 📂
- Enhanced error dialog details shown when starting a full scan without any active monitored directories
---
.../org/pwss/controller/HomeController.java | 8 +++++---
.../table/MonitoredDirectoryTableModel.java | 2 +-
.../java/org/pwss/service/ScanService.java | 9 ++++-----
src/main/java/org/pwss/utils/ErrorUtils.java | 18 ++++++++++++++++++
.../java/org/pwss/utils/StringConstants.java | 4 ++--
5 files changed, 30 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/org/pwss/utils/ErrorUtils.java
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 9315817..a7fce75 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -61,6 +61,7 @@
import org.pwss.service.ScanSummaryService;
import org.pwss.utils.AppTheme;
import org.pwss.utils.ConversionUtils;
+import org.pwss.utils.ErrorUtils;
import org.pwss.utils.LiveFeedUtils;
import org.pwss.utils.MonitoredDirectoryUtils;
import org.pwss.utils.OSUtils;
@@ -71,7 +72,6 @@
import org.pwss.view.screen.HomeScreen;
import org.slf4j.LoggerFactory;
-
import static org.pwss.app_settings.AppConfig.APP_THEME;
import static org.pwss.app_settings.AppConfig.MAX_HASH_EXTRACTION_FILE_SIZE;
import static org.pwss.app_settings.AppConfig.USE_SPLASH_SCREEN;
@@ -603,7 +603,8 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
screen.getMaxHashExtractionFileSizeUnlimitedCheckbox().setSelected(false);
screen.getMaxHashExtractionFileSizeSlider().setEnabled(true);
- final int maxSliderValueMegabytes = Math.toIntExact(ConversionUtils.bytesToMegabytes(maxFileSizeForHashExtraction));
+ final int maxSliderValueMegabytes = Math
+ .toIntExact(ConversionUtils.bytesToMegabytes(maxFileSizeForHashExtraction));
screen.getMaxHashExtractionFileSizeSlider().setValue(maxSliderValueMegabytes);
screen.getMaxHashExtractionFileSizeValueLabel().setText(maxSliderValueMegabytes + " MB");
} else {
@@ -686,7 +687,8 @@ public void performStartScan(boolean singleDirectory) {
| JsonProcessingException e) {
log.debug(StringConstants.SCAN_START_ERROR, e);
log.error(StringConstants.SCAN_START_ERROR + " {}", e.getMessage());
- SwingUtilities.invokeLater(() -> screen.showError(StringConstants.SCAN_START_ERROR));
+ SwingUtilities.invokeLater(() -> screen
+ .showError(ErrorUtils.formatErrorMessage(StringConstants.SCAN_START_ERROR, e.getMessage())));
}
}
diff --git a/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java b/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java
index 4c3edc8..8f2d2c2 100644
--- a/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java
+++ b/src/main/java/org/pwss/model/table/MonitoredDirectoryTableModel.java
@@ -9,7 +9,7 @@
public class MonitoredDirectoryTableModel extends AbstractTableModel {
private final List directories;
private final String[] columnNames = {
- "\uD83D\uDCC1 Path", "\uD83D\uDEA6 Note", "\uD83D\uDD59 Last Scanned", "\uD83D\uDEE1️ Baseline Established", "\uD83D\uDDC2️ Include Subdirectories", "Active"
+ "\uD83D\uDCC1 Path", "\uD83D\uDCDD Note", "\uD83D\uDD59 Last Scanned", "\u2693 Baseline Established", "\uD83D\uDCC2 Include Subdirectories", "\uD83D\uDD0C Active"
};
public MonitoredDirectoryTableModel(List directories) {
diff --git a/src/main/java/org/pwss/service/ScanService.java b/src/main/java/org/pwss/service/ScanService.java
index 2411a0d..7220406 100644
--- a/src/main/java/org/pwss/service/ScanService.java
+++ b/src/main/java/org/pwss/service/ScanService.java
@@ -6,7 +6,6 @@
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
-import org.pwss.app_settings.AppConfig;
import org.pwss.exception.scan.GetAllMostRecentScansException;
import org.pwss.exception.scan.GetMostRecentScansException;
import org.pwss.exception.scan.GetScanDiffsException;
@@ -54,12 +53,12 @@ public boolean startScan(long maxHashExtractionFileSize) throws StartFullScanExc
return switch (response.statusCode()) {
case 200 -> true;
case 401 ->
- throw new StartFullScanException("Start scan all failed: User not authorized to perform this action.");
+ throw new StartFullScanException("User not authorized to perform this action.");
case 412 ->
- throw new StartFullScanException("Start scan all failed: No active monitored directories found.");
- case 425 -> throw new StartFullScanException("Start scan all failed: Scan is already running.");
+ throw new StartFullScanException("There are no directories being actively monitored.");
+ case 425 -> throw new StartFullScanException("Scan is already running.");
case 500 ->
- throw new StartFullScanException("Start scan all failed: An error occurred on the server while attempting to start the scan.");
+ throw new StartFullScanException("An error occurred on the server while attempting to start the scan.");
default -> false;
};
}
diff --git a/src/main/java/org/pwss/utils/ErrorUtils.java b/src/main/java/org/pwss/utils/ErrorUtils.java
new file mode 100644
index 0000000..f9ae267
--- /dev/null
+++ b/src/main/java/org/pwss/utils/ErrorUtils.java
@@ -0,0 +1,18 @@
+package org.pwss.utils;
+
+/**
+ * Utility class for handling and formatting error messages.
+ */
+public final class ErrorUtils {
+
+ /**
+ * Formats an error message by combining a constant text with a dynamic error message.
+ *
+ * @param errorConstantText The constant part of the error message, such as "Error: "
+ * @param errorMessageText The dynamic part of the error message that provides specific details
+ * @return A formatted error message string combining both parts
+ */
+ public static String formatErrorMessage(String errorConstantText, String errorMessageText) {
+ return errorConstantText + errorMessageText;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/pwss/utils/StringConstants.java b/src/main/java/org/pwss/utils/StringConstants.java
index 25c68ac..f6f087f 100644
--- a/src/main/java/org/pwss/utils/StringConstants.java
+++ b/src/main/java/org/pwss/utils/StringConstants.java
@@ -27,8 +27,8 @@ public final class StringConstants {
public static final String SCAN_STOPPED_SUCCESS = "Scan stopped successfully!";
public static final String SCAN_STARTED_FAILURE = "Failed to start scan.";
public static final String SCAN_STOPPED_FAILURE = "Failed to stop scan.";
- public static final String SCAN_START_ERROR = "Error starting scan: ";
- public static final String SCAN_STOP_ERROR = "Error stopping scan: ";
+ public static final String SCAN_START_ERROR = "An error occurred during scan initiation: ";
+ public static final String SCAN_STOP_ERROR = "An error has occurred while attempting to stop the scan.: ";
public static final String SCAN_BASELINE_COMPLETED = "Baseline established successfully!\nDo you wish to see the details?";
public static final String SCAN_COMPLETED_DIFFS = "Scan completed with differences found.\nDo you wish to see the details?\nYou can always view results later in the recent scans table.";
public static final String SCAN_COMPLETED_NO_DIFFS = "Scan completed with no differences found.\nDo you wish to see the details?\nYou can always view results later in the recent scans table.";
From 408947ca51863a1676ac60da60bef590ac74084e Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Mon, 3 Nov 2025 23:57:05 +0100
Subject: [PATCH 24/44] Live feed output has been enhanced
---
src/main/java/org/pwss/utils/ErrorUtils.java | 9 +++--
.../java/org/pwss/utils/LiveFeedUtils.java | 35 +++++++++++++++++--
src/main/java/org/pwss/utils/OSUtils.java | 1 -
3 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/pwss/utils/ErrorUtils.java b/src/main/java/org/pwss/utils/ErrorUtils.java
index f9ae267..beefac1 100644
--- a/src/main/java/org/pwss/utils/ErrorUtils.java
+++ b/src/main/java/org/pwss/utils/ErrorUtils.java
@@ -6,10 +6,13 @@
public final class ErrorUtils {
/**
- * Formats an error message by combining a constant text with a dynamic error message.
+ * Formats an error message by combining a constant text with a dynamic error
+ * message.
*
- * @param errorConstantText The constant part of the error message, such as "Error: "
- * @param errorMessageText The dynamic part of the error message that provides specific details
+ * @param errorConstantText The constant part of the error message, such as
+ * "Error: "
+ * @param errorMessageText The dynamic part of the error message that provides
+ * specific details
* @return A formatted error message string combining both parts
*/
public static String formatErrorMessage(String errorConstantText, String errorMessageText) {
diff --git a/src/main/java/org/pwss/utils/LiveFeedUtils.java b/src/main/java/org/pwss/utils/LiveFeedUtils.java
index c687f7b..859a62d 100644
--- a/src/main/java/org/pwss/utils/LiveFeedUtils.java
+++ b/src/main/java/org/pwss/utils/LiveFeedUtils.java
@@ -1,7 +1,37 @@
package org.pwss.utils;
+/**
+ * Utility class for processing live feed entries.
+ */
public final class LiveFeedUtils {
+ /**
+ * A white check mark emoji used in live feed entries.
+ */
+ private final static String WHITE_CHECK_MARK = "✅";
+ /**
+ * The white check mark emoji with a newline character appended to it.
+ */
+ private final static String WHITE_CHECK_MARK_REPLACE = "✅\n";
+
+ /**
+ * A warning emoji used in live feed entries.
+ */
+ private final static String WARNING = "⚠️";
+ /**
+ * The warning emoji with a newline character appended to it.
+ */
+ private final static String WARNING_REPLACE = "⚠️\n";
+
+ /**
+ * A message indicating that a file is too big, used in live feed entries.
+ */
+ private final static String FILE_TO_BIG_MESSAGE = "is bigger than the user defined max limit";
+ /**
+ * The file too big message with a newline character appended to it.
+ */
+ private final static String FILE_TO_BIG_MESSAGE_REPLACE = FILE_TO_BIG_MESSAGE + "\n";
+
/**
* Formats a raw live feed entry for improved readability.
* Currently, inserts line breaks after certain emojis.
@@ -15,8 +45,9 @@ public static String formatLiveFeedEntry(String rawEntry) {
}
return rawEntry
- .replace("✅", "✅\n")
- .replace("⚠️", "⚠️\n")
+ .replace(WHITE_CHECK_MARK, WHITE_CHECK_MARK_REPLACE)
+ .replace(WARNING, WARNING_REPLACE)
+ .replace(FILE_TO_BIG_MESSAGE, FILE_TO_BIG_MESSAGE_REPLACE)
.trim();
}
diff --git a/src/main/java/org/pwss/utils/OSUtils.java b/src/main/java/org/pwss/utils/OSUtils.java
index b86fa59..e0e07d5 100644
--- a/src/main/java/org/pwss/utils/OSUtils.java
+++ b/src/main/java/org/pwss/utils/OSUtils.java
@@ -105,7 +105,6 @@ public static final boolean isMac() {
* @return A warning message string specific to the current OS.
*/
public static String getQuarantineWarningMessage() {
- // TODO: Replace with custom, detailed warning messages for each OS.
return switch (determineOSType()) {
case WINDOWS ->
"Warning: Quarantining or removing Windows system files (drivers, DLLs, registry-related files) can render the system unstable or unbootable. Back up data, ensure you have recovery media and administrator access before proceeding.";
From 4e219e209193290416b24a9bcb097d7e9a158770 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Tue, 4 Nov 2025 19:42:50 +0100
Subject: [PATCH 25/44] Changes to LoginUtils and new unit tests, WIP welcome
message
---
.../org/pwss/controller/LoginController.java | 51 +++---
src/main/java/org/pwss/navigation/Screen.java | 2 +-
src/main/java/org/pwss/utils/LoginUtils.java | 122 +++++++++----
.../org/pwss/view/screen/LoginScreen.form | 161 ++++++++++--------
.../org/pwss/view/screen/LoginScreen.java | 147 +++++++++++++---
.../java/org/pwss/utils/LoginUtilsTest.java | 108 ++++++++----
6 files changed, 407 insertions(+), 184 deletions(-)
diff --git a/src/main/java/org/pwss/controller/LoginController.java b/src/main/java/org/pwss/controller/LoginController.java
index ec5f3bb..a1432e4 100644
--- a/src/main/java/org/pwss/controller/LoginController.java
+++ b/src/main/java/org/pwss/controller/LoginController.java
@@ -59,8 +59,8 @@ public LoginController(LoginScreen view) {
@Override
public void onShow() {
- getScreen().getUsernameField().setText("");
- getScreen().getPasswordField().setText("");
+ screen.getUsernameField().setText("");
+ screen.getPasswordField().setText("");
log.debug("Current LICENSE_KEY: {}", licenseKeySet ? "SET" : "NOT SET");
log.debug("Create User Mode: {}", createUserMode);
refreshView();
@@ -68,32 +68,36 @@ public void onShow() {
@Override
protected void initListeners() {
- getScreen().getPasswordField().addActionListener(e -> proceedAndValidate());
- getScreen().getProceedButton().addActionListener(e -> proceedAndValidate());
- getScreen().getCancelButton().addActionListener(e -> System.exit(0));
+ screen.getPasswordField().addActionListener(e -> proceedAndValidate());
+ screen.getProceedButton().addActionListener(e -> proceedAndValidate());
+ screen.getCancelButton().addActionListener(e -> System.exit(0));
}
@Override
protected void refreshView() {
// Only show license key fields if LICENSE_KEY is not set
if (licenseKeySet) {
- getScreen().getLicenseLabel().setVisible(false);
- getScreen().getLicenseKeyField().setVisible(false);
+ screen.getLicenseLabel().setVisible(false);
+ screen.getLicenseKeyField().setVisible(false);
}
// Update the view based on whether we are in create user mode
if (createUserMode) {
// Notify user that no users exist and they need to create one
- getScreen().showInfo("No user found.\nCreate one by entering a username and password.");
+ screen.showInfo("No user found.\nCreate one by entering a username and password.");
// Update message label to indicate user creation
- getScreen().setMessage("Create a user for the first login.");
+ screen.setMessage("Create a user for the first login.");
// Change button text to "Register"
- getScreen().getProceedButton().setText("Register");
+ screen.getProceedButton().setText("Register");
} else {
// Update message label to indicate normal login
- getScreen().setMessage("Login with your username and password.");
+ screen.setMessage("Login with your username and password.");
// Change button text to "Login"
- getScreen().getProceedButton().setText("Login");
+ screen.getProceedButton().setText("Login");
}
+
+ // Show or hide confirm password fields based on create user mode
+ screen.getConfirmPasswordLabel().setVisible(createUserMode);
+ screen.getConfirmPasswordField().setVisible(createUserMode);
}
/**
@@ -150,13 +154,14 @@ private void proceedAndValidate() {
* @return true if input is valid, false otherwise.
*/
private boolean validateInput() {
- String username = getScreen().getUsername();
- String password = getScreen().getPassword();
- String licenseKey = licenseKeySet ? LICENSE_KEY : getScreen().getLicenseKey();
+ String username = screen.getUsername();
+ String password = screen.getPassword();
+ String confirmPassword = screen.getConfirmPassword();
+ String licenseKey = licenseKeySet ? LICENSE_KEY : screen.getLicenseKey();
- LoginUtils.LoginValidationResult result = LoginUtils.validateInput(username, password, licenseKey, createUserMode);
+ LoginUtils.LoginValidationResult result = LoginUtils.validateInput(username, password, confirmPassword, licenseKey, createUserMode);
if (!result.isValid()) {
- getScreen().showError(result.errorMessage());
+ screen.showError(LoginUtils.formatErrors(result.errors()));
}
return result.isValid();
@@ -177,9 +182,9 @@ private void createUserAndLogin() {
* @return true if user creation is successful, false otherwise.
*/
private boolean createUser() {
- String username = getScreen().getUsername();
- String password = getScreen().getPassword();
- String licenseKey = licenseKeySet ? LICENSE_KEY : getScreen().getLicenseKey();
+ String username = screen.getUsername();
+ String password = screen.getPassword();
+ String licenseKey = licenseKeySet ? LICENSE_KEY : screen.getLicenseKey();
try {
final boolean createSuccess = authService.createUser(username, password, licenseKey);
@@ -215,9 +220,9 @@ private boolean createUser() {
* Displays success or error messages based on the outcome.
*/
private void performLogin() {
- String username = getScreen().getUsername();
- String password = getScreen().getPassword();
- String licenseKey = licenseKeySet ? LICENSE_KEY : getScreen().getLicenseKey();
+ String username = screen.getUsername();
+ String password = screen.getPassword();
+ String licenseKey = licenseKeySet ? LICENSE_KEY : screen.getLicenseKey();
try {
final boolean loginSuccess = authService.login(username, password, licenseKey);
diff --git a/src/main/java/org/pwss/navigation/Screen.java b/src/main/java/org/pwss/navigation/Screen.java
index 36092fc..b00ac91 100644
--- a/src/main/java/org/pwss/navigation/Screen.java
+++ b/src/main/java/org/pwss/navigation/Screen.java
@@ -7,7 +7,7 @@ public enum Screen {
/**
* The login screen where users can authenticate.
*/
- LOGIN(400, 200),
+ LOGIN(450, 350),
/**
* The home screen displayed after successful login.
diff --git a/src/main/java/org/pwss/utils/LoginUtils.java b/src/main/java/org/pwss/utils/LoginUtils.java
index 7bc0539..a0a002f 100644
--- a/src/main/java/org/pwss/utils/LoginUtils.java
+++ b/src/main/java/org/pwss/utils/LoginUtils.java
@@ -1,5 +1,9 @@
package org.pwss.utils;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Utility class for validating login inputs.
*/
@@ -9,45 +13,101 @@ public final class LoginUtils {
private LoginUtils() {
// This constructor is intentionally empty.
}
+
/**
- * Result of login validation.
+ * Validates the login input fields.
*
- * @param isValid True if the input is valid, false otherwise.
- * @param errorMessage The error message if invalid, null if valid.
+ * @param username The username input.
+ * @param password The password input.
+ * @param confirmPassword The confirm password input (used in create user mode).
+ * @param licenseKey The license key input.
+ * @param createUserMode Flag indicating if the application is in create user mode.
+ * @return A LoginValidationResult containing validation status and error messages.
*/
- public record LoginValidationResult(boolean isValid, String errorMessage) {}
+ public static LoginValidationResult validateInput(
+ String username,
+ String password,
+ String confirmPassword,
+ String licenseKey,
+ boolean createUserMode
+ ) {
+ List errors = new ArrayList<>();
+
+ username = username == null ? "" : username.trim();
+ password = password == null ? "" : password.trim();
+ confirmPassword = confirmPassword == null ? "" : confirmPassword.trim();
+ licenseKey = licenseKey == null ? "" : licenseKey.trim();
+
+ if (username.isEmpty()) errors.add("Username cannot be empty.");
+ if (password.isEmpty()) errors.add("Password cannot be empty.");
+ if (licenseKey.isEmpty()) errors.add("License key cannot be empty.");
+
+ if (createUserMode && !password.isEmpty()) {
+ if (password.length() < 8)
+ errors.add("Password must be at least 8 characters long.");
+
+ if (confirmPassword.isEmpty())
+ errors.add("Confirm Password cannot be empty.");
+ else if (!xorEquals(password, confirmPassword))
+ errors.add("Password and Confirm Password do not match.");
+
+ if (!password.matches(".*[A-Z].*"))
+ errors.add("Password must contain at least one uppercase letter.");
+ if (!password.matches(".*\\d.*"))
+ errors.add("Password must contain at least one digit.");
+ if (!password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/?].*"))
+ errors.add("Password must contain at least one special character.");
+ }
+
+ return new LoginValidationResult(errors.isEmpty(), errors);
+ }
/**
- * Validates the user input for username, password, and license key.
+ * Formats a list of error messages into a single string.
*
- * @param username The username input.
- * @param password The password input.
- * @param licenseKey The license key input.
- * @param createUserMode True if in user creation mode, false otherwise.
- * @return A LoginValidationResult indicating whether the input is valid and any error message.
+ * @param errors List of error messages.
+ * @return Formatted error string.
*/
- public static LoginValidationResult validateInput(String username, String password, String licenseKey, boolean createUserMode) {
- if (username == null || username.trim().isEmpty()) {
- return new LoginValidationResult(false, "Username cannot be empty.");
- }
- if (password == null || password.trim().isEmpty()) {
- return new LoginValidationResult(false, "Password cannot be empty.");
- }
- if (createUserMode && password.trim().length() < 8) {
- return new LoginValidationResult(false, "Password must be at least 8 characters long.");
- }
- if (createUserMode && !password.matches(".*[A-Z].*")) {
- return new LoginValidationResult(false, "Password must contain at least one uppercase letter.");
- }
- if (createUserMode && !password.matches(".*\\d.*")) {
- return new LoginValidationResult(false, "Password must contain at least one digit.");
- }
- if (createUserMode && !password.matches(".*[!@#$%^&*(),.?\":{}|<>].*")) {
- return new LoginValidationResult(false, "Password must contain at least one special character.");
+ public static String formatErrors(List errors) {
+ return String.join("\n", errors);
+ }
+
+
+ /**
+ * Compares two strings for equality using XOR operation.
+ *
+ * @param s1 First string.
+ * @param s2 Second string.
+ * @return True if both strings are equal, false otherwise.
+ * @throws IllegalArgumentException if either string is null.
+ */
+ private static boolean xorEquals(String s1, String s2) {
+ if (s1 == null || s2 == null) {
+ throw new IllegalArgumentException("Strings to compare cannot be null");
}
- if (licenseKey == null || licenseKey.trim().isEmpty()) {
- return new LoginValidationResult(false, "License key cannot be empty.");
+
+ byte[] a = s1.getBytes(StandardCharsets.UTF_8);
+ byte[] b = s2.getBytes(StandardCharsets.UTF_8);
+
+ int maxLen = Math.max(a.length, b.length);
+ int result = a.length ^ b.length;
+
+ for (int i = 0; i < maxLen; i++) {
+ byte ba = (i < a.length) ? a[i] : 0;
+ byte bb = (i < b.length) ? b[i] : 0;
+ result |= (ba ^ bb);
}
- return new LoginValidationResult(true, null);
+
+ return result == 0;
+ }
+
+ /**
+ * Result of login validation.
+ *
+ * @param isValid True if the input is valid, false otherwise.
+ * @param errors List of error messages if the input is invalid.
+ */
+ public record LoginValidationResult(boolean isValid, List errors) {
}
}
+
diff --git a/src/main/java/org/pwss/view/screen/LoginScreen.form b/src/main/java/org/pwss/view/screen/LoginScreen.form
index f3ace95..8877f19 100644
--- a/src/main/java/org/pwss/view/screen/LoginScreen.form
+++ b/src/main/java/org/pwss/view/screen/LoginScreen.form
@@ -1,97 +1,122 @@
diff --git a/src/main/java/org/pwss/view/screen/LoginScreen.java b/src/main/java/org/pwss/view/screen/LoginScreen.java
index 3cbeef4..4342a04 100644
--- a/src/main/java/org/pwss/view/screen/LoginScreen.java
+++ b/src/main/java/org/pwss/view/screen/LoginScreen.java
@@ -2,6 +2,7 @@
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.Spacer;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
@@ -30,6 +31,8 @@ public class LoginScreen extends BaseScreen {
private JPanel rootPanel;
private JTextField licenseKeyField;
private JLabel licenseLabel;
+ private JPasswordField confirmPasswordField;
+ private JLabel confirmPasswordLabel;
@Override
protected String getScreenName() {
@@ -41,45 +44,123 @@ public JPanel getRootPanel() {
return rootPanel;
}
+ /**
+ * Sets the message to be displayed on the login screen.
+ *
+ * @param message the message to display
+ */
+ public void setMessage(String message) {
+ this.messageLabel.setText(message);
+ }
+
+ /**
+ * Gets the username entered by the user.
+ *
+ * @return the username as a String
+ */
public String getUsername() {
return usernameField.getText().trim();
}
+ /**
+ * Gets the password entered by the user.
+ *
+ * @return the password as a String
+ */
public String getPassword() {
return new String(passwordField.getPassword());
}
+ /**
+ * Gets the confirm password entered by the user.
+ *
+ * @return the confirm password as a String
+ */
+ public String getConfirmPassword() {
+ return new String(confirmPasswordField.getPassword());
+ }
+
+ /**
+ * Gets the license key entered by the user.
+ *
+ * @return the license key as a String
+ */
public String getLicenseKey() {
return licenseKeyField.getText().trim();
}
+ /**
+ * Gets the proceed button.
+ *
+ * @return the proceed JButton
+ */
public JButton getProceedButton() {
return proceedButton;
}
+ /**
+ * Gets the cancel button.
+ *
+ * @return the cancel JButton
+ */
public JButton getCancelButton() {
return cancelButton;
}
+ /**
+ * Gets the password field.
+ *
+ * @return the password JPasswordField
+ */
+ public JPasswordField getPasswordField() {
+ return passwordField;
+ }
+
+ /**
+ * Gets the username label.
+ *
+ * @return the username JLabel
+ */
public JTextField getUsernameField() {
return usernameField;
}
- public JPasswordField getPasswordField() {
- return passwordField;
+ /**
+ * Gets the confirm password label.
+ *
+ * @return the confirm password JLabel
+ */
+ public JLabel getConfirmPasswordLabel() {
+ return confirmPasswordLabel;
}
- public void setMessage(String message) {
- this.messageLabel.setText(message);
+ /**
+ * Gets the confirm password field.
+ *
+ * @return the confirm password JPasswordField
+ */
+ public JPasswordField getConfirmPasswordField() {
+ return confirmPasswordField;
}
+ /**
+ * Gets the license label.
+ *
+ * @return the license JLabel
+ */
+ public JLabel getLicenseLabel() {
+ return licenseLabel;
+ }
+
+ /**
+ * Gets the license key text field.
+ *
+ * @return the license key JTextField
+ */
public JTextField getLicenseKeyField() {
return licenseKeyField;
}
- public JLabel getLicenseLabel() {
- return licenseLabel;
- }
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
@@ -97,43 +178,53 @@ public JLabel getLicenseLabel() {
*/
private void $$$setupUI$$$() {
rootPanel = new JPanel();
- rootPanel.setLayout(new GridLayoutManager(5, 3, new Insets(10, 10, 10, 10), -1, -1, false, true));
- usernameLabel = new JLabel();
- Font usernameLabelFont = this.$$$getFont$$$(null, Font.BOLD, -1, usernameLabel.getFont());
- if (usernameLabelFont != null) usernameLabel.setFont(usernameLabelFont);
- usernameLabel.setText("Username");
- rootPanel.add(usernameLabel, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- passwordLabel = new JLabel();
- Font passwordLabelFont = this.$$$getFont$$$(null, Font.BOLD, -1, passwordLabel.getFont());
- if (passwordLabelFont != null) passwordLabel.setFont(passwordLabelFont);
- passwordLabel.setText("Password");
- rootPanel.add(passwordLabel, new GridConstraints(2, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ rootPanel.setLayout(new GridLayoutManager(6, 3, new Insets(10, 10, 10, 10), -1, -1));
final JPanel panel1 = new JPanel();
- panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
- rootPanel.add(panel1, new GridConstraints(4, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ panel1.setLayout(new GridLayoutManager(10, 3, new Insets(0, 0, 0, 0), -1, -1));
+ rootPanel.add(panel1, new GridConstraints(0, 0, 6, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
+ final JPanel panel2 = new JPanel();
+ panel2.setLayout(new GridLayoutManager(1, 2, new Insets(10, 0, 0, 0), -1, -1, true, false));
+ panel1.add(panel2, new GridConstraints(9, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
proceedButton = new JButton();
proceedButton.setText("Button");
- panel1.add(proceedButton, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel2.add(proceedButton, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
cancelButton = new JButton();
cancelButton.setText("Cancel");
- panel1.add(cancelButton, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel2.add(cancelButton, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
messageLabel = new JLabel();
- Font messageLabelFont = this.$$$getFont$$$(null, Font.PLAIN, -1, messageLabel.getFont());
+ Font messageLabelFont = this.$$$getFont$$$(null, Font.BOLD, 12, messageLabel.getFont());
if (messageLabelFont != null) messageLabel.setFont(messageLabelFont);
messageLabel.setText("");
- rootPanel.add(messageLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel1.add(messageLabel, new GridConstraints(0, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
passwordField = new JPasswordField();
- rootPanel.add(passwordField, new GridConstraints(2, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel1.add(passwordField, new GridConstraints(4, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
usernameField = new JTextField();
usernameField.setText("");
- rootPanel.add(usernameField, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel1.add(usernameField, new GridConstraints(2, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
licenseKeyField = new JTextField();
- rootPanel.add(licenseKeyField, new GridConstraints(3, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
+ panel1.add(licenseKeyField, new GridConstraints(8, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
+ confirmPasswordField = new JPasswordField();
+ panel1.add(confirmPasswordField, new GridConstraints(6, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ passwordLabel = new JLabel();
+ Font passwordLabelFont = this.$$$getFont$$$(null, Font.BOLD, -1, passwordLabel.getFont());
+ if (passwordLabelFont != null) passwordLabel.setFont(passwordLabelFont);
+ passwordLabel.setText("Password");
+ panel1.add(passwordLabel, new GridConstraints(3, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ confirmPasswordLabel = new JLabel();
+ Font confirmPasswordLabelFont = this.$$$getFont$$$(null, Font.BOLD, -1, confirmPasswordLabel.getFont());
+ if (confirmPasswordLabelFont != null) confirmPasswordLabel.setFont(confirmPasswordLabelFont);
+ confirmPasswordLabel.setText("Confirm password");
+ panel1.add(confirmPasswordLabel, new GridConstraints(5, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
licenseLabel = new JLabel();
Font licenseLabelFont = this.$$$getFont$$$(null, Font.BOLD, -1, licenseLabel.getFont());
if (licenseLabelFont != null) licenseLabel.setFont(licenseLabelFont);
licenseLabel.setText("License key");
- rootPanel.add(licenseLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel1.add(licenseLabel, new GridConstraints(7, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ usernameLabel = new JLabel();
+ Font usernameLabelFont = this.$$$getFont$$$(null, Font.BOLD, -1, usernameLabel.getFont());
+ if (usernameLabelFont != null) usernameLabel.setFont(usernameLabelFont);
+ usernameLabel.setText("Username");
+ panel1.add(usernameLabel, new GridConstraints(1, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
}
/**
diff --git a/src/test/java/org/pwss/utils/LoginUtilsTest.java b/src/test/java/org/pwss/utils/LoginUtilsTest.java
index ad19b2a..b7ca8d6 100644
--- a/src/test/java/org/pwss/utils/LoginUtilsTest.java
+++ b/src/test/java/org/pwss/utils/LoginUtilsTest.java
@@ -1,68 +1,110 @@
package org.pwss.utils;
+import java.util.List;
import org.junit.jupiter.api.Test;
+import org.pwss.utils.LoginUtils.LoginValidationResult;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class LoginUtilsTest {
- @Test
- void validateInput_returnsValidResult_whenAllInputsAreValid() {
- var result = LoginUtils.validateInput("validUser", "Valid123!", "LICENSE123", false);
- assertTrue(result.isValid());
- assertNull(result.errorMessage());
+ // --- Helper for readable assertions ---
+ private void assertHasError(LoginValidationResult result, String expectedError) {
+ assertFalse(result.isValid(), "Expected validation to fail");
+ assertTrue(result.errors().contains(expectedError),
+ "Expected error message: " + expectedError);
}
@Test
- void validateInput_returnsError_whenUsernameIsEmpty() {
- var result = LoginUtils.validateInput("", "Valid123!", "LICENSE123", false);
+ void testAllFieldsEmpty() {
+ LoginValidationResult result = LoginUtils.validateInput("", "", "", "", false);
assertFalse(result.isValid());
- assertEquals("Username cannot be empty.", result.errorMessage());
+ assertEquals(3, result.errors().size());
+ assertHasError(result, "Username cannot be empty.");
+ assertHasError(result, "Password cannot be empty.");
+ assertHasError(result, "License key cannot be empty.");
}
@Test
- void validateInput_returnsError_whenPasswordIsEmpty() {
- var result = LoginUtils.validateInput("validUser", "", "LICENSE123", false);
- assertFalse(result.isValid());
- assertEquals("Password cannot be empty.", result.errorMessage());
+ void testNullInputsHandledGracefully() {
+ assertDoesNotThrow(() -> {
+ LoginValidationResult result = LoginUtils.validateInput(null, null, null, null, false);
+ assertFalse(result.isValid());
+ assertTrue(result.errors().contains("Username cannot be empty."));
+ });
}
+ // --- License key validation ---
+
@Test
- void validateInput_returnsError_whenPasswordTooShortInCreateUserMode() {
- var result = LoginUtils.validateInput("validUser", "Short1!", "LICENSE123", true);
- assertFalse(result.isValid());
- assertEquals("Password must be at least 8 characters long.", result.errorMessage());
+ void testMissingLicenseKey() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "password", "", "", false);
+ assertHasError(result, "License key cannot be empty.");
}
+ // --- Password validation in createUserMode ---
+
@Test
- void validateInput_returnsError_whenPasswordMissingUppercaseInCreateUserMode() {
- var result = LoginUtils.validateInput("validUser", "valid123!", "LICENSE123", true);
- assertFalse(result.isValid());
- assertEquals("Password must contain at least one uppercase letter.", result.errorMessage());
+ void testPasswordTooShort() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "Ab1!", "Ab1!", "key123", true);
+ assertHasError(result, "Password must be at least 8 characters long.");
}
@Test
- void validateInput_returnsError_whenPasswordMissingDigitInCreateUserMode() {
- var result = LoginUtils.validateInput("validUser", "ValidPass!", "LICENSE123", true);
- assertFalse(result.isValid());
- assertEquals("Password must contain at least one digit.", result.errorMessage());
+ void testConfirmPasswordMissing() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "Abcd123!", "", "key123", true);
+ assertHasError(result, "Confirm Password cannot be empty.");
}
@Test
- void validateInput_returnsError_whenPasswordMissingSpecialCharacterInCreateUserMode() {
- var result = LoginUtils.validateInput("validUser", "Valid1234", "LICENSE123", true);
- assertFalse(result.isValid());
- assertEquals("Password must contain at least one special character.", result.errorMessage());
+ void testPasswordsDoNotMatch() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "Abcd123!", "Abcd1234!", "key123", true);
+ assertHasError(result, "Password and Confirm Password do not match.");
}
@Test
- void validateInput_returnsError_whenLicenseKeyIsEmpty() {
- var result = LoginUtils.validateInput("validUser", "Valid123!", "", false);
- assertFalse(result.isValid());
- assertEquals("License key cannot be empty.", result.errorMessage());
+ void testPasswordMissingUppercase() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "abcd123!", "abcd123!", "key123", true);
+ assertHasError(result, "Password must contain at least one uppercase letter.");
+ }
+
+ @Test
+ void testPasswordMissingDigit() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "Abcdefg!", "Abcdefg!", "key123", true);
+ assertHasError(result, "Password must contain at least one digit.");
+ }
+
+ @Test
+ void testPasswordMissingSpecialCharacter() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "Abcdefg1", "Abcdefg1", "key123", true);
+ assertHasError(result, "Password must contain at least one special character.");
+ }
+
+ // --- Valid case ---
+
+ @Test
+ void testValidInputInCreateUserMode() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "Abcd123!", "Abcd123!", "key123", true);
+ assertTrue(result.isValid(), "Expected validation to pass");
+ assertTrue(result.errors().isEmpty(), "Expected no errors");
+ }
+
+ @Test
+ void testValidInputInLoginMode() {
+ LoginValidationResult result = LoginUtils.validateInput("user", "password", "", "key123", false);
+ assertTrue(result.isValid(), "Expected validation to pass in login mode");
+ }
+
+ // --- formatErrors() ---
+
+ @Test
+ void testFormatErrors() {
+ List errors = List.of("Error 1", "Error 2", "Error 3");
+ String formatted = LoginUtils.formatErrors(errors);
+ assertEquals("Error 1\nError 2\nError 3", formatted);
}
}
From a88893918f69763130d4b82c8770422fa1d6ce05 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Tue, 4 Nov 2025 19:53:15 +0100
Subject: [PATCH 26/44] Welcome dialog when creating user reminding of
importance of remembering credentials
---
.../org/pwss/controller/LoginController.java | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/controller/LoginController.java b/src/main/java/org/pwss/controller/LoginController.java
index a1432e4..e4c69bc 100644
--- a/src/main/java/org/pwss/controller/LoginController.java
+++ b/src/main/java/org/pwss/controller/LoginController.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.concurrent.ExecutionException;
+import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.pwss.app_settings.AppConfig;
import org.pwss.exception.user.CreateUserException;
@@ -11,6 +12,7 @@
import org.pwss.navigation.Screen;
import org.pwss.service.AuthService;
import org.pwss.utils.LoginUtils;
+import org.pwss.utils.StringConstants;
import org.pwss.view.screen.LoginScreen;
import org.slf4j.LoggerFactory;
@@ -141,7 +143,19 @@ private void proceedAndValidate() {
if (createUserMode) {
// Handle user creation logic here and then login
- createUserAndLogin();
+ int choice = screen.showOptionDialog(JOptionPane.INFORMATION_MESSAGE,
+ "Welcome to Integrity Hash!\n\n" +
+ "You are about to create the first user for this application.\n" +
+ "Please make sure to remember your credentials as they will be required for future logins.\n\n" +
+ "Do you want to proceed?",
+ new String[]{StringConstants.GENERIC_YES, StringConstants.GENERIC_NO},
+ StringConstants.GENERIC_YES);
+
+ if (choice == 0) {
+ createUserAndLogin();
+ } else {
+ log.debug("User creation cancelled by user.");
+ }
} else {
// Handle normal login logic here
performLogin();
From c6d5bcc4289f0fc9ac3fd94c3d857c93eabe3b22 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Tue, 4 Nov 2025 22:25:46 +0100
Subject: [PATCH 27/44] Fix dynamic frame size depending on if createUser mode
or regular mode in login screen :)
---
.../org/pwss/controller/BaseController.java | 22 ++---
.../org/pwss/controller/HomeController.java | 3 +
.../org/pwss/controller/LoginController.java | 3 +
.../controller/NewDirectoryController.java | 1 +
.../controller/ScanDetailsController.java | 1 +
.../pwss/navigation/NavigationHandler.java | 1 +
src/main/java/org/pwss/navigation/Screen.java | 2 +-
.../java/org/pwss/view/screen/BaseScreen.java | 25 ++++-
.../java/org/pwss/view/screen/HomeScreen.java | 2 +-
.../org/pwss/view/screen/LoginScreen.form | 98 +++++++++----------
.../org/pwss/view/screen/LoginScreen.java | 62 ++++++------
.../pwss/view/screen/NewDirectoryScreen.java | 4 +-
.../pwss/view/screen/ScanDetailsScreen.java | 4 +-
13 files changed, 127 insertions(+), 101 deletions(-)
diff --git a/src/main/java/org/pwss/controller/BaseController.java b/src/main/java/org/pwss/controller/BaseController.java
index 580d966..2e632b0 100644
--- a/src/main/java/org/pwss/controller/BaseController.java
+++ b/src/main/java/org/pwss/controller/BaseController.java
@@ -16,12 +16,14 @@ public abstract class BaseController {
* Represents the UI component associated with this controller.
*/
protected Screen screen;
-
/**
* The navigation context for passing data between different parts of the application during navigation.
*/
private NavigationContext context;
-
+ /**
+ * Logger instance for logging purposes.
+ */
+ private final org.slf4j.Logger log;
/**
* Constructs a `BaseController` with the specified view.
* Initializes the view and sets up event listeners.
@@ -30,12 +32,12 @@ public abstract class BaseController {
*/
public BaseController(Screen screen) {
this.screen = screen;
+ this.log = org.slf4j.LoggerFactory.getLogger(BaseController.class);
// Run onCreate lifecycle method
onCreate();
// Initialize event listeners
initListeners();
}
-
/**
* Retrieves the current navigation context.
*
@@ -44,7 +46,6 @@ public BaseController(Screen screen) {
protected NavigationContext getContext() {
return context;
}
-
/**
* Sets the navigation context for the controller.
*
@@ -53,26 +54,22 @@ protected NavigationContext getContext() {
public void setContext(NavigationContext context) {
this.context = context;
}
-
/**
* Abstract method to initialize event listeners for the Screen.
* Subclasses must provide an implementation for this method.
*/
abstract void initListeners();
-
/**
* Abstract method to refresh or update the view.
* Subclasses must provide an implementation for this method.
*/
abstract void refreshView();
-
/**
* Method to reload or refresh data displayed in the view.
* Subclasses can override this method to provide specific data reloading logic.
- * The default implementation does nothing.
*/
public void reloadData() {
- // Default implementation does nothing
+ log.debug("reloadData called for {}", screen.getScreenName());
}
/**
@@ -83,22 +80,19 @@ public void reloadData() {
public Screen getScreen() {
return screen;
}
-
/**
* Method called when the view is shown.
* Subclasses can override this method to perform actions when the view becomes visible.
- * The default implementation does nothing.
*/
public void onShow() {
- // Default implementation does nothing
+ log.debug("onShow called for {}", screen.getScreenName());
}
/**
* Method called when the view is created.
* Subclasses can override this method to perform actions during the creation of the view.
- * The default implementation does nothing.
*/
public void onCreate() {
- // Default implementation does nothing
+ log.debug("onCreate called for {}", screen.getScreenName());
}
}
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index a7fce75..fec95a6 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -195,6 +195,7 @@ public HomeController(HomeScreen view) {
@Override
public void onCreate() {
+ super.onCreate();
// Update theme picker
screen.getThemePicker().removeAllItems();
// Populate the combo box with AppTheme values
@@ -236,6 +237,7 @@ public Component getListCellRendererComponent(JList> list, Object value,
@Override
public void onShow() {
+ super.onShow();
fetchDataAndRefreshView();
}
@@ -304,6 +306,7 @@ private List safeGetDiffs(long scanId) {
@Override
public void reloadData() {
+ super.reloadData();
fetchDataAndRefreshView();
}
diff --git a/src/main/java/org/pwss/controller/LoginController.java b/src/main/java/org/pwss/controller/LoginController.java
index e4c69bc..1da9ed8 100644
--- a/src/main/java/org/pwss/controller/LoginController.java
+++ b/src/main/java/org/pwss/controller/LoginController.java
@@ -61,10 +61,13 @@ public LoginController(LoginScreen view) {
@Override
public void onShow() {
+ super.onShow();
screen.getUsernameField().setText("");
screen.getPasswordField().setText("");
log.debug("Current LICENSE_KEY: {}", licenseKeySet ? "SET" : "NOT SET");
log.debug("Create User Mode: {}", createUserMode);
+ // Adjust frame size based on create user mode
+ screen.getParentFrame().setSize(450, createUserMode ? 300 : 250);
refreshView();
}
diff --git a/src/main/java/org/pwss/controller/NewDirectoryController.java b/src/main/java/org/pwss/controller/NewDirectoryController.java
index 35f3c74..8370474 100644
--- a/src/main/java/org/pwss/controller/NewDirectoryController.java
+++ b/src/main/java/org/pwss/controller/NewDirectoryController.java
@@ -36,6 +36,7 @@ public NewDirectoryController(NewDirectoryScreen screen) {
@Override
public void onShow() {
+ super.onShow();
// Reset selected path when the screen is shown
selectedPath = null;
refreshView();
diff --git a/src/main/java/org/pwss/controller/ScanDetailsController.java b/src/main/java/org/pwss/controller/ScanDetailsController.java
index 1222bfd..a7975fc 100644
--- a/src/main/java/org/pwss/controller/ScanDetailsController.java
+++ b/src/main/java/org/pwss/controller/ScanDetailsController.java
@@ -66,6 +66,7 @@ public ScanDetailsController(ScanDetailsScreen screen) {
@Override
public void onShow() {
+ super.onShow();
if (scanSummaries != null && !scanSummaries.isEmpty()) {
// Clear existing data
scanSummaries = List.of();
diff --git a/src/main/java/org/pwss/navigation/NavigationHandler.java b/src/main/java/org/pwss/navigation/NavigationHandler.java
index 36530a7..661ef7c 100644
--- a/src/main/java/org/pwss/navigation/NavigationHandler.java
+++ b/src/main/java/org/pwss/navigation/NavigationHandler.java
@@ -50,6 +50,7 @@ public void navigateTo(Screen screen, NavigationContext context) {
// Set the navigation context for the controller
controller.setContext(context);
BaseScreen baseScreen = controller.getScreen();
+ baseScreen.setParentFrame(frame);
// Ensure the controller reloads its data when navigating to the screen
controller.reloadData();
diff --git a/src/main/java/org/pwss/navigation/Screen.java b/src/main/java/org/pwss/navigation/Screen.java
index b00ac91..bb9c5b9 100644
--- a/src/main/java/org/pwss/navigation/Screen.java
+++ b/src/main/java/org/pwss/navigation/Screen.java
@@ -7,7 +7,7 @@ public enum Screen {
/**
* The login screen where users can authenticate.
*/
- LOGIN(450, 350),
+ LOGIN(450, 250),
/**
* The home screen displayed after successful login.
diff --git a/src/main/java/org/pwss/view/screen/BaseScreen.java b/src/main/java/org/pwss/view/screen/BaseScreen.java
index 223b4e2..f00a5f7 100644
--- a/src/main/java/org/pwss/view/screen/BaseScreen.java
+++ b/src/main/java/org/pwss/view/screen/BaseScreen.java
@@ -1,5 +1,6 @@
package org.pwss.view.screen;
+import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
@@ -10,13 +11,35 @@
* @author PWSS ORG
*/
public abstract class BaseScreen extends JPanel {
+ /**
+ * The parent JFrame of this screen.
+ */
+ private JFrame parentFrame;
+
+ /**
+ * Set the parent JFrame of this screen.
+ *
+ * @param parentFrame The parent JFrame to set.
+ */
+ public void setParentFrame(JFrame parentFrame) {
+ this.parentFrame = parentFrame;
+ }
+
+ /**
+ * Get the parent JFrame of this screen.
+ *
+ * @return The parent JFrame.
+ */
+ public JFrame getParentFrame() {
+ return parentFrame;
+ }
/**
* Get the screen name for dialog titles & logging purposes.
*
* @return The name of the screen.
*/
- protected abstract String getScreenName();
+ public abstract String getScreenName();
/**
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index bda50d4..e809c9e 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -229,7 +229,7 @@ public class HomeScreen extends BaseScreen {
private JCheckBox maxHashExtractionFileSizeUnlimitedCheckbox;
@Override
- protected String getScreenName() {
+ public String getScreenName() {
return "Home";
}
diff --git a/src/main/java/org/pwss/view/screen/LoginScreen.form b/src/main/java/org/pwss/view/screen/LoginScreen.form
index 8877f19..16535c0 100644
--- a/src/main/java/org/pwss/view/screen/LoginScreen.form
+++ b/src/main/java/org/pwss/view/screen/LoginScreen.form
@@ -1,6 +1,6 @@
*/
-public class ConversionUtils {
+public class ConversionUtil {
private static final long MEGABYTE = 1024L * 1024L;
private static final long GIGABYTE = MEGABYTE * 1024L;
diff --git a/src/main/java/org/pwss/utils/ErrorUtils.java b/src/main/java/org/pwss/util/ErrorUtil.java
similarity index 92%
rename from src/main/java/org/pwss/utils/ErrorUtils.java
rename to src/main/java/org/pwss/util/ErrorUtil.java
index beefac1..5338971 100644
--- a/src/main/java/org/pwss/utils/ErrorUtils.java
+++ b/src/main/java/org/pwss/util/ErrorUtil.java
@@ -1,9 +1,9 @@
-package org.pwss.utils;
+package org.pwss.util;
/**
* Utility class for handling and formatting error messages.
*/
-public final class ErrorUtils {
+public final class ErrorUtil {
/**
* Formats an error message by combining a constant text with a dynamic error
diff --git a/src/main/java/org/pwss/utils/LiveFeedUtils.java b/src/main/java/org/pwss/util/LiveFeedUtil.java
similarity index 97%
rename from src/main/java/org/pwss/utils/LiveFeedUtils.java
rename to src/main/java/org/pwss/util/LiveFeedUtil.java
index 859a62d..15ee9fc 100644
--- a/src/main/java/org/pwss/utils/LiveFeedUtils.java
+++ b/src/main/java/org/pwss/util/LiveFeedUtil.java
@@ -1,9 +1,9 @@
-package org.pwss.utils;
+package org.pwss.util;
/**
* Utility class for processing live feed entries.
*/
-public final class LiveFeedUtils {
+public final class LiveFeedUtil {
/**
* A white check mark emoji used in live feed entries.
diff --git a/src/main/java/org/pwss/utils/LoginUtils.java b/src/main/java/org/pwss/util/LoginUtil.java
similarity index 97%
rename from src/main/java/org/pwss/utils/LoginUtils.java
rename to src/main/java/org/pwss/util/LoginUtil.java
index a0a002f..4cd2d01 100644
--- a/src/main/java/org/pwss/utils/LoginUtils.java
+++ b/src/main/java/org/pwss/util/LoginUtil.java
@@ -1,4 +1,4 @@
-package org.pwss.utils;
+package org.pwss.util;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -7,10 +7,10 @@
/**
* Utility class for validating login inputs.
*/
-public final class LoginUtils {
+public final class LoginUtil {
// Private constructor to prevent instantiation
- private LoginUtils() {
+ private LoginUtil() {
// This constructor is intentionally empty.
}
diff --git a/src/main/java/org/pwss/utils/MonitoredDirectoryUtils.java b/src/main/java/org/pwss/util/MonitoredDirectoryUtil.java
similarity index 78%
rename from src/main/java/org/pwss/utils/MonitoredDirectoryUtils.java
rename to src/main/java/org/pwss/util/MonitoredDirectoryUtil.java
index 19e5dcf..73507b1 100644
--- a/src/main/java/org/pwss/utils/MonitoredDirectoryUtils.java
+++ b/src/main/java/org/pwss/util/MonitoredDirectoryUtil.java
@@ -1,7 +1,10 @@
-package org.pwss.utils;
+package org.pwss.util;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
+import java.util.LinkedList;
import java.util.List;
import org.pwss.model.entity.MonitoredDirectory;
import org.slf4j.Logger;
@@ -9,17 +12,17 @@
/**
* Utility class for handling operations related to monitored directories.
*/
-public final class MonitoredDirectoryUtils {
+public final class MonitoredDirectoryUtil {
/**
* Logger instance for logging purposes
*/
- private final static Logger log = org.slf4j.LoggerFactory.getLogger(MonitoredDirectoryUtils.class);
+ private final static Logger log = org.slf4j.LoggerFactory.getLogger(MonitoredDirectoryUtil.class);
/**
* Private constructor to prevent instantiation
*/
- private MonitoredDirectoryUtils() {
+ private MonitoredDirectoryUtil() {
}
/**
@@ -98,4 +101,25 @@ public static boolean isScanOlderThan1Minute(MonitoredDirectory dir) {
return lastScan.isBefore(oneMinuteAgo);
}
+
+ /**
+ * Filters a list of monitored directories based on whether their paths exist in
+ * the filesystem.
+ *
+ * @param inputList The list of monitored directories to be filtered.
+ * @return A new list containing only the directories from the input list that
+ * have valid, confirmed paths.
+ */
+ public static List filterMonitoredDirectoriesOnConfirmedPath(
+ List inputList) {
+
+ List mDirectories = new LinkedList<>();
+
+ for (MonitoredDirectory m : inputList) {
+ if (Files.exists(Path.of(m.path()))) {
+ mDirectories.add(m);
+ }
+ }
+ return mDirectories;
+ }
}
diff --git a/src/main/java/org/pwss/utils/OSUtils.java b/src/main/java/org/pwss/util/OSUtil.java
similarity index 98%
rename from src/main/java/org/pwss/utils/OSUtils.java
rename to src/main/java/org/pwss/util/OSUtil.java
index e0e07d5..15da0ae 100644
--- a/src/main/java/org/pwss/utils/OSUtils.java
+++ b/src/main/java/org/pwss/util/OSUtil.java
@@ -1,13 +1,13 @@
-package org.pwss.utils;
+package org.pwss.util;
/**
* Utility class for operating system detection and information.
* Provides methods to determine the current OS type and related functionalities.
*/
-public final class OSUtils {
+public final class OSUtil {
// Private constructor to prevent instantiation
- private OSUtils() {
+ private OSUtil() {
throw new UnsupportedOperationException("Utility class cannot be instantiated");
}
diff --git a/src/main/java/org/pwss/utils/ReportUtils.java b/src/main/java/org/pwss/util/ReportUtil.java
similarity index 98%
rename from src/main/java/org/pwss/utils/ReportUtils.java
rename to src/main/java/org/pwss/util/ReportUtil.java
index 687f88e..06a8dc2 100644
--- a/src/main/java/org/pwss/utils/ReportUtils.java
+++ b/src/main/java/org/pwss/util/ReportUtil.java
@@ -1,4 +1,4 @@
-package org.pwss.utils;
+package org.pwss.util;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -8,7 +8,7 @@
import org.pwss.model.entity.Scan;
import org.pwss.model.entity.ScanSummary;
-public class ReportUtils {
+public class ReportUtil {
/**
* Formats a ScanSummary into a human-readable string.
diff --git a/src/main/java/org/pwss/utils/StringConstants.java b/src/main/java/org/pwss/util/StringConstants.java
similarity index 99%
rename from src/main/java/org/pwss/utils/StringConstants.java
rename to src/main/java/org/pwss/util/StringConstants.java
index d4a74be..69fae7a 100644
--- a/src/main/java/org/pwss/utils/StringConstants.java
+++ b/src/main/java/org/pwss/util/StringConstants.java
@@ -1,4 +1,4 @@
-package org.pwss.utils;
+package org.pwss.util;
/**
* A utility class that holds various string constants used throughout the application.
diff --git a/src/main/java/org/pwss/utils/StringUtils.java b/src/main/java/org/pwss/util/StringUtil.java
similarity index 97%
rename from src/main/java/org/pwss/utils/StringUtils.java
rename to src/main/java/org/pwss/util/StringUtil.java
index beff46a..e7a10ac 100644
--- a/src/main/java/org/pwss/utils/StringUtils.java
+++ b/src/main/java/org/pwss/util/StringUtil.java
@@ -1,16 +1,16 @@
-package org.pwss.utils;
+package org.pwss.util;
/**
* A utility class for string manipulation operations.
* This class contains static methods to perform various string manipulations.
*/
-public final class StringUtils {
+public final class StringUtil {
/**
*
* Private constructor to prevent instantiation
**/
- private StringUtils() {
+ private StringUtil() {
}
/**
diff --git a/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java b/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
index b43e090..cecb7b6 100644
--- a/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
+++ b/src/main/java/org/pwss/view/popup_menu/MonitoredDirectoryPopupFactory.java
@@ -18,8 +18,8 @@
import org.pwss.model.entity.MonitoredDirectory;
import org.pwss.model.request.notes.RestoreNoteType;
import org.pwss.model.table.MonitoredDirectoryTableModel;
-import org.pwss.utils.StringConstants;
-import org.pwss.utils.StringUtils;
+import org.pwss.util.StringConstants;
+import org.pwss.util.StringUtil;
import org.pwss.view.popup_menu.listener.MonitoredDirectoryPopupListener;
/**
@@ -146,7 +146,7 @@ private JMenuItem getUpdateNoteItem(MonitoredDirectory dir) {
updateNoteItem.addActionListener(e -> {
// Label for directory path
JLabel label = new JLabel(
- StringConstants.MON_DIR_POPUP_UPDATE_NOTE_POPUP_PREFIX + StringUtils.prependSpace(dir.path()));
+ StringConstants.MON_DIR_POPUP_UPDATE_NOTE_POPUP_PREFIX + StringUtil.prependSpace(dir.path()));
label.setAlignmentX(Component.LEFT_ALIGNMENT);
// Text area for note input
diff --git a/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java b/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java
index 1080fab..52b07d9 100644
--- a/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java
+++ b/src/main/java/org/pwss/view/popup_menu/listener/MonitoredDirectoryPopupListenerImpl.java
@@ -6,7 +6,7 @@
import org.pwss.model.request.notes.RestoreNoteType;
import org.pwss.service.MonitoredDirectoryService;
import org.pwss.service.NoteService;
-import org.pwss.utils.StringConstants;
+import org.pwss.util.StringConstants;
import org.slf4j.LoggerFactory;
/**
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index e809c9e..78bbd28 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -28,7 +28,7 @@
import javax.swing.plaf.FontUIResource;
import javax.swing.text.StyleContext;
import org.pwss.model.entity.MonitoredDirectory;
-import org.pwss.utils.AppTheme;
+import org.pwss.util.AppTheme;
/**
* The HomeScreen class represents the main screen of the application.
diff --git a/src/test/java/org/pwss/utils/LoginUtilsTest.java b/src/test/java/org/pwss/util/LoginUtilTest.java
similarity index 67%
rename from src/test/java/org/pwss/utils/LoginUtilsTest.java
rename to src/test/java/org/pwss/util/LoginUtilTest.java
index b7ca8d6..de79da5 100644
--- a/src/test/java/org/pwss/utils/LoginUtilsTest.java
+++ b/src/test/java/org/pwss/util/LoginUtilTest.java
@@ -1,16 +1,15 @@
-package org.pwss.utils;
+package org.pwss.util;
import java.util.List;
import org.junit.jupiter.api.Test;
-import org.pwss.utils.LoginUtils.LoginValidationResult;
-
+import org.pwss.util.LoginUtil.LoginValidationResult;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class LoginUtilsTest {
+public class LoginUtilTest {
// --- Helper for readable assertions ---
private void assertHasError(LoginValidationResult result, String expectedError) {
@@ -21,7 +20,7 @@ private void assertHasError(LoginValidationResult result, String expectedError)
@Test
void testAllFieldsEmpty() {
- LoginValidationResult result = LoginUtils.validateInput("", "", "", "", false);
+ LoginValidationResult result = LoginUtil.validateInput("", "", "", "", false);
assertFalse(result.isValid());
assertEquals(3, result.errors().size());
assertHasError(result, "Username cannot be empty.");
@@ -32,7 +31,7 @@ void testAllFieldsEmpty() {
@Test
void testNullInputsHandledGracefully() {
assertDoesNotThrow(() -> {
- LoginValidationResult result = LoginUtils.validateInput(null, null, null, null, false);
+ LoginValidationResult result = LoginUtil.validateInput(null, null, null, null, false);
assertFalse(result.isValid());
assertTrue(result.errors().contains("Username cannot be empty."));
});
@@ -42,7 +41,7 @@ void testNullInputsHandledGracefully() {
@Test
void testMissingLicenseKey() {
- LoginValidationResult result = LoginUtils.validateInput("user", "password", "", "", false);
+ LoginValidationResult result = LoginUtil.validateInput("user", "password", "", "", false);
assertHasError(result, "License key cannot be empty.");
}
@@ -50,37 +49,37 @@ void testMissingLicenseKey() {
@Test
void testPasswordTooShort() {
- LoginValidationResult result = LoginUtils.validateInput("user", "Ab1!", "Ab1!", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "Ab1!", "Ab1!", "key123", true);
assertHasError(result, "Password must be at least 8 characters long.");
}
@Test
void testConfirmPasswordMissing() {
- LoginValidationResult result = LoginUtils.validateInput("user", "Abcd123!", "", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "Abcd123!", "", "key123", true);
assertHasError(result, "Confirm Password cannot be empty.");
}
@Test
void testPasswordsDoNotMatch() {
- LoginValidationResult result = LoginUtils.validateInput("user", "Abcd123!", "Abcd1234!", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "Abcd123!", "Abcd1234!", "key123", true);
assertHasError(result, "Password and Confirm Password do not match.");
}
@Test
void testPasswordMissingUppercase() {
- LoginValidationResult result = LoginUtils.validateInput("user", "abcd123!", "abcd123!", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "abcd123!", "abcd123!", "key123", true);
assertHasError(result, "Password must contain at least one uppercase letter.");
}
@Test
void testPasswordMissingDigit() {
- LoginValidationResult result = LoginUtils.validateInput("user", "Abcdefg!", "Abcdefg!", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "Abcdefg!", "Abcdefg!", "key123", true);
assertHasError(result, "Password must contain at least one digit.");
}
@Test
void testPasswordMissingSpecialCharacter() {
- LoginValidationResult result = LoginUtils.validateInput("user", "Abcdefg1", "Abcdefg1", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "Abcdefg1", "Abcdefg1", "key123", true);
assertHasError(result, "Password must contain at least one special character.");
}
@@ -88,14 +87,14 @@ void testPasswordMissingSpecialCharacter() {
@Test
void testValidInputInCreateUserMode() {
- LoginValidationResult result = LoginUtils.validateInput("user", "Abcd123!", "Abcd123!", "key123", true);
+ LoginValidationResult result = LoginUtil.validateInput("user", "Abcd123!", "Abcd123!", "key123", true);
assertTrue(result.isValid(), "Expected validation to pass");
assertTrue(result.errors().isEmpty(), "Expected no errors");
}
@Test
void testValidInputInLoginMode() {
- LoginValidationResult result = LoginUtils.validateInput("user", "password", "", "key123", false);
+ LoginValidationResult result = LoginUtil.validateInput("user", "password", "", "key123", false);
assertTrue(result.isValid(), "Expected validation to pass in login mode");
}
@@ -104,7 +103,7 @@ void testValidInputInLoginMode() {
@Test
void testFormatErrors() {
List errors = List.of("Error 1", "Error 2", "Error 3");
- String formatted = LoginUtils.formatErrors(errors);
+ String formatted = LoginUtil.formatErrors(errors);
assertEquals("Error 1\nError 2\nError 3", formatted);
}
}
diff --git a/src/test/java/org/pwss/utils/OSUtilsTest.java b/src/test/java/org/pwss/util/OSUtilTest.java
similarity index 75%
rename from src/test/java/org/pwss/utils/OSUtilsTest.java
rename to src/test/java/org/pwss/util/OSUtilTest.java
index 3507f25..7c279ad 100644
--- a/src/test/java/org/pwss/utils/OSUtilsTest.java
+++ b/src/test/java/org/pwss/util/OSUtilTest.java
@@ -1,4 +1,4 @@
-package org.pwss.utils;
+package org.pwss.util;
import org.junit.jupiter.api.Test;
@@ -6,11 +6,11 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
-public class OSUtilsTest {
+public class OSUtilTest {
@Test
void testNullInput() {
- assertNull(OSUtils.formatQuarantinePath(null), "Null input should return null");
+ assertNull(OSUtil.formatQuarantinePath(null), "Null input should return null");
}
@Test
@@ -18,13 +18,13 @@ void testSimplePathConversion() {
final String input = "usr.local.bin.myfile.txt";
final String expected;
- if (OSUtils.isWindows()) {
+ if (OSUtil.isWindows()) {
expected = "usr\\local\\bin\\myfile.txt";
} else {
expected = "usr/local/bin/myfile.txt";
}
- assertEquals(expected, OSUtils.formatQuarantinePath(input),
+ assertEquals(expected, OSUtil.formatQuarantinePath(input),
"Path separators should be converted correctly for the current OS");
}
@@ -33,13 +33,13 @@ void testDoubleDotPreservation() {
final String input = "etc..config.file.txt";
final String expected;
- if (OSUtils.isWindows()) {
+ if (OSUtil.isWindows()) {
expected = "etc\\.config\\file.txt";
} else {
expected = "etc/.config/file.txt";
}
- assertEquals(expected, OSUtils.formatQuarantinePath(input),
+ assertEquals(expected, OSUtil.formatQuarantinePath(input),
"Double dots should be preserved correctly as literal dots in the output");
}
@@ -48,13 +48,13 @@ void testExtensionPreservation() {
final String input = "var.log.myapp.log";
final String expected;
- if (OSUtils.isWindows()) {
+ if (OSUtil.isWindows()) {
expected = "var\\log\\myapp.log";
} else {
expected = "var/log/myapp.log";
}
- assertEquals(expected, OSUtils.formatQuarantinePath(input),
+ assertEquals(expected, OSUtil.formatQuarantinePath(input),
"File extensions should be preserved with a single dot");
}
@@ -63,7 +63,7 @@ void testComplexMixedPath() {
final String input;
final String expected;
- if (OSUtils.isWindows()) {
+ if (OSUtil.isWindows()) {
input = "C_drive__.Program.Files.Java.myapp.jar";
expected = "C:\\Program\\Files\\Java\\myapp.jar";
} else {
@@ -71,7 +71,7 @@ void testComplexMixedPath() {
expected = "etc/.config/.file.txt";
}
- assertEquals(expected, OSUtils.formatQuarantinePath(input),
+ assertEquals(expected, OSUtil.formatQuarantinePath(input),
"Complex paths should convert consistently across OS types");
}
}
From f3f4754fb4a0fdd6f0bb129eba22e81b9594cfdf Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Mon, 17 Nov 2025 16:14:46 +0100
Subject: [PATCH 39/44] feat(gui): improve UI/UX and enhance diff workflow
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Updated file icons
- Adjusted full-scan flow so “Scan with differences” now navigates to the **Recent Diff** tab after user confirmation
- Improved live feed accountability and clarity
- Added visible diff count to the warning/confirmation dialog after directory scans
- Performed minor UI/UX refinements and general cleanup
---
.../org/pwss/controller/HomeController.java | 16 +++++++-
src/main/java/org/pwss/util/ScanUtil.java | 37 +++++++++++++++++++
.../java/org/pwss/util/StringConstants.java | 1 -
src/main/java/org/pwss/util/StringUtil.java | 2 +-
.../java/org/pwss/view/screen/HomeScreen.java | 2 +-
5 files changed, 53 insertions(+), 5 deletions(-)
create mode 100644 src/main/java/org/pwss/util/ScanUtil.java
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 174c3df..756f767 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -1,6 +1,7 @@
package org.pwss.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
+
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ItemEvent;
@@ -66,6 +67,7 @@
import org.pwss.util.MonitoredDirectoryUtil;
import org.pwss.util.OSUtil;
import org.pwss.util.ReportUtil;
+import org.pwss.util.ScanUtil;
import org.pwss.util.StringConstants;
import org.pwss.view.popup_menu.MonitoredDirectoryPopupFactory;
import org.pwss.view.popup_menu.listener.MonitoredDirectoryPopupListenerImpl;
@@ -723,7 +725,8 @@ private void onFinishScan(boolean completed, boolean singleDirectory) {
int choice;
// Prompt the user to view scan results based on whether differences were found
if (totalDiffCount.get() > 0) {
- choice = screen.showOptionDialog(JOptionPane.WARNING_MESSAGE, StringConstants.SCAN_COMPLETED_DIFFS,
+ choice = screen.showOptionDialog(JOptionPane.WARNING_MESSAGE,
+ ScanUtil.constructDiffMessageString(totalDiffCount.get()),
new String[] { StringConstants.GENERIC_YES, StringConstants.GENERIC_NO },
StringConstants.GENERIC_YES);
} else {
@@ -772,7 +775,7 @@ private void onFinishScan(boolean completed, boolean singleDirectory) {
if (totalDiffCount.get() > 0) {
// If full scan, and we have diffs, navigate to the diffs tab to show all
// differences.
- screen.getTabbedPane().setSelectedIndex(2);
+ screen.getTabbedPane().setSelectedIndex(3);
} else {
// If no diffs, navigate to the recent scans tab to show the most recent scan.
screen.getTabbedPane().setSelectedIndex(0);
@@ -845,13 +848,22 @@ private void startPollingScanLiveFeed(boolean singleDirectory, List
Date: Mon, 17 Nov 2025 18:49:01 +0100
Subject: [PATCH 40/44] Update ScanTableModel Status column icon
---
src/main/java/org/pwss/model/table/ScanTableModel.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/model/table/ScanTableModel.java b/src/main/java/org/pwss/model/table/ScanTableModel.java
index a9ae6b4..6603d17 100644
--- a/src/main/java/org/pwss/model/table/ScanTableModel.java
+++ b/src/main/java/org/pwss/model/table/ScanTableModel.java
@@ -8,7 +8,7 @@
public class ScanTableModel extends AbstractTableModel {
private final List scans;
private final String[] columnNames = {
- "\uD83D\uDCC1 Directory", "\uD83D\uDD59 Scan Time", "\uD83D\uDEA6 Status"
+ "\uD83D\uDCC1 Directory", "\uD83D\uDD59 Scan Time", "⌛ Status"
};
public ScanTableModel(List scans) {
From 4b85b27341213ec7e9adec8044511e3ef6fe5af8 Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Mon, 17 Nov 2025 22:03:40 +0100
Subject: [PATCH 41/44] Added RingBuffer Data Structure
- Introduced a new RingBuffer class as a circular data structure for efficient data handling.
- Integrated the RingBuffer into the `startPollingScanLiveFeed` method in HomeController to reduce memory overhead
and improve system performance.
---
.../org/pwss/controller/HomeController.java | 64 +++++++++------
.../org/pwss/data_structure/RingBuffer.java | 78 +++++++++++++++++++
2 files changed, 120 insertions(+), 22 deletions(-)
create mode 100644 src/main/java/org/pwss/data_structure/RingBuffer.java
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 756f767..a12481d 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -25,6 +25,7 @@
import javax.swing.Timer;
import org.pwss.app_settings.AppConfig;
import org.pwss.controller.util.NavigationContext;
+import org.pwss.data_structure.RingBuffer;
import org.pwss.exception.metadata.MetadataKeyNameRetrievalException;
import org.pwss.exception.monitored_directory.MonitoredDirectoryGetAllException;
import org.pwss.exception.scan.GetAllMostRecentScansException;
@@ -827,51 +828,70 @@ private void startPollingScanLiveFeed(boolean singleDirectory, List liveFeedBuffer = new RingBuffer<>(100);
+
+ // Create and start a polling timer
scanStatusTimer = new Timer(1000, e -> {
try {
- // Retrieve live feed updates
+ // Get live feed from backend
LiveFeedResponse liveFeed = scanService.getLiveFeed();
- String currentLiveFeedText = screen.getLiveFeedText().getText();
+ // Format the new update from the backend
String newEntry = LiveFeedUtil.formatLiveFeedEntry(liveFeed.livefeed());
- String updatedLiveFeedText = currentLiveFeedText + newEntry;
- screen.getLiveFeedText().setText(updatedLiveFeedText);
- // Update the total difference count based on new warnings
+ // Add the new update to the ring buffer
+ liveFeedBuffer.add(newEntry);
+
+ // Build the text for the live feed based on the latest updates in the buffer
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < liveFeedBuffer.size(); i++) {
+ sb.append(liveFeedBuffer.get(i));
+ }
+
+ // Update UI with the latest text
+ screen.getLiveFeedText().setText(sb.toString());
+
+ // Update the diff counter
totalDiffCount.addAndGet(LiveFeedUtil.countWarnings(liveFeed.livefeed()));
screen.getLiveFeedDiffCount().setText(StringConstants.SCAN_DIFFS_PREFIX + totalDiffCount);
- // Update scanRunning state and refresh the UI if necessary
+ // Update scan status and UI as needed
if (liveFeed.isScanRunning() != scanRunning) {
scanRunning = liveFeed.isScanRunning();
refreshView();
}
- if (!liveFeed.isScanRunning()) {
-
- log.debug("Stopped pulling the live feed due to completion.");
- scanStatusTimer.stop(); // Terminate polling when the scan completes
- log.debug("Retrieve the live feed one last time after receiving the final update from the server.");
- liveFeed = scanService.getLiveFeed();
+ // When the scan is complete, do a final pull
+ if (!liveFeed.isScanRunning()) {
+ log.debug("Scan completed, making final pull...");
- totalDiffCount.getAndAdd(LiveFeedUtil.countWarnings(liveFeed.livefeed()));
- screen.getLiveFeedDiffCount().setText(StringConstants.SCAN_DIFFS_PREFIX + totalDiffCount);
+ // Do a final pull without time pressure
+ liveFeed = scanService.getLiveFeed(); // The last pull to get any final updates
+ // Add the last update to the buffer
newEntry = LiveFeedUtil.formatLiveFeedEntry(liveFeed.livefeed());
if (!org.pwss.util.StringUtil.isEmpty(newEntry)) {
- currentLiveFeedText = screen.getLiveFeedText().getText();
- updatedLiveFeedText = currentLiveFeedText + newEntry;
- screen.getLiveFeedText().setText(updatedLiveFeedText);
+ liveFeedBuffer.add(newEntry); // Add the last pull to the buffer
+
+ // Update UI with the last text
+ sb.setLength(0); // Rensa StringBuilder
+ for (int i = 0; i < liveFeedBuffer.size(); i++) {
+ sb.append(liveFeedBuffer.get(i)); // Add all updates to the buffer
+ }
+ screen.getLiveFeedText().setText(sb.toString());
}
- onFinishScan(true, singleDirectory);
+ // Stop polling and end the process
+ scanStatusTimer.stop();
+ onFinishScan(true, singleDirectory); // Complete the scan
}
} catch (LiveFeedException | ExecutionException | InterruptedException | JsonProcessingException ex) {
- log.error(StringConstants.SCAN_LIVE_FEED_ERROR_PREFIX + " {}", ex.getMessage());
- log.debug("Debug Live Feed Exception", ex);
- SwingUtilities.invokeLater(() -> screen.showError(StringConstants.SCAN_LIVE_FEED_ERROR_PREFIX));
+ log.error("Error fetching live feed: {}", ex.getMessage());
+ log.debug("Live Feed Exception", ex);
+ SwingUtilities.invokeLater(() -> screen.showError("Live feed error"));
scanStatusTimer.stop(); // Stop polling on error
- onFinishScan(false, singleDirectory);
+ onFinishScan(false, singleDirectory); // Handling errors
}
});
scanStatusTimer.start();
diff --git a/src/main/java/org/pwss/data_structure/RingBuffer.java b/src/main/java/org/pwss/data_structure/RingBuffer.java
new file mode 100644
index 0000000..becde28
--- /dev/null
+++ b/src/main/java/org/pwss/data_structure/RingBuffer.java
@@ -0,0 +1,78 @@
+package org.pwss.data_structure;
+
+/**
+ * A thread-safe, fixed-size ring buffer implementation.
+ *
+ * @param The type of elements held in this collection.
+ */
+public final class RingBuffer {
+ private final Object[] buffer;
+ private int writeIndex = 0;
+ private int readIndex = 0;
+ private int size = 0;
+
+ /**
+ * Constructs a new ring buffer with the specified capacity.
+ *
+ * @param capacity The maximum number of elements that can be stored in this
+ * buffer.
+ */
+ public RingBuffer(int capacity) {
+ buffer = new Object[capacity];
+ }
+
+ /**
+ * Adds an item to the end of the buffer. If the buffer is full, this method
+ * will overwrite the oldest element with the new one.
+ *
+ * @param item The item to add to the buffer.
+ */
+ public void add(T item) {
+ buffer[writeIndex] = item;
+ writeIndex = (writeIndex + 1) % buffer.length;
+
+ if (size == buffer.length) {
+ // Buffer is full, move read pointer forward.
+ readIndex = (readIndex + 1) % buffer.length;
+ } else {
+ size++;
+ }
+ }
+
+ /**
+ * Retrieves the item at the specified index from this buffer. The first element
+ * in
+ * the buffer has an index of zero.
+ *
+ * @param index The index of the item to retrieve.
+ * @return The item at the specified position in this buffer.
+ * @throws IndexOutOfBoundsException If the specified index is greater than or
+ * equal to the size of this
+ * buffer.
+ */
+ public T get(int index) {
+ if (index >= size) {
+ throw new IndexOutOfBoundsException();
+ }
+ int realIndex = (readIndex + index) % buffer.length;
+ return (T) buffer[realIndex];
+ }
+
+ /**
+ * Returns the number of elements in this buffer.
+ *
+ * @return The current number of elements in this buffer.
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Checks whether this buffer is empty or not.
+ *
+ * @return True if this buffer contains no elements; false otherwise.
+ */
+ public boolean isEmpty() {
+ return size == 0;
+ }
+}
\ No newline at end of file
From 41d6dd6a2f9eebb49fed92db3282e40c6501135f Mon Sep 17 00:00:00 2001
From: Stefan
Date: Mon, 17 Nov 2025 22:10:33 +0100
Subject: [PATCH 42/44] Beginning of diff count label
---
.../org/pwss/controller/HomeController.java | 4 +
.../controller/ScanDetailsController.java | 4 +
.../java/org/pwss/view/screen/HomeScreen.form | 12 +-
.../java/org/pwss/view/screen/HomeScreen.java | 298 +++++-------------
.../pwss/view/screen/ScanDetailsScreen.form | 12 +-
.../pwss/view/screen/ScanDetailsScreen.java | 62 ++--
6 files changed, 141 insertions(+), 251 deletions(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index a12481d..3d1b4c8 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -534,6 +534,10 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
screen.getMonitoredDirectoriesTable().setModel(monitoredDirectoryTableModel);
screen.getMonitoredDirectoriesTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ // Update diffs count label
+ // TODO: Update with count from endpoint when available
+ screen.getDiffsCountLabel().setText("Diffs found: " + (recentDiffs != null ? recentDiffs.size() : 0));
+
DiffTableModel diffTableModel = new DiffTableModel(recentDiffs != null ? recentDiffs : List.of());
screen.getDiffTable().setModel(diffTableModel);
screen.getDiffTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
diff --git a/src/main/java/org/pwss/controller/ScanDetailsController.java b/src/main/java/org/pwss/controller/ScanDetailsController.java
index 2710de6..593f663 100644
--- a/src/main/java/org/pwss/controller/ScanDetailsController.java
+++ b/src/main/java/org/pwss/controller/ScanDetailsController.java
@@ -141,6 +141,10 @@ void refreshView() {
SimpleSummaryTableModel simpleSummaryTableModel = new SimpleSummaryTableModel(scanSummaries);
screen.getScanSummaryTable().setModel(simpleSummaryTableModel);
+ // Update diffs count label
+ // TODO: Update with count from endpoint when available
+ screen.getDiffsCountLabel().setText("Diffs found: " + diffs.size());
+
// Populate diffs table
DiffTableModel diffTableModel = new DiffTableModel(diffs);
screen.getDiffTable().setModel(diffTableModel);
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.form b/src/main/java/org/pwss/view/screen/HomeScreen.form
index 9c6ae80..f77d6c5 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.form
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.form
@@ -375,7 +375,7 @@
-
+
@@ -385,7 +385,7 @@
-
+
@@ -432,6 +432,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/pwss/view/screen/HomeScreen.java b/src/main/java/org/pwss/view/screen/HomeScreen.java
index 0d238a9..25c5cb0 100644
--- a/src/main/java/org/pwss/view/screen/HomeScreen.java
+++ b/src/main/java/org/pwss/view/screen/HomeScreen.java
@@ -228,6 +228,11 @@ public class HomeScreen extends BaseScreen {
*/
private JCheckBox maxHashExtractionFileSizeUnlimitedCheckbox;
+ /**
+ * Label to display the count of differences (diffs) detected.
+ */
+ private JLabel diffsCountLabel;
+
@Override
public String getScreenName() {
return "Home";
@@ -504,16 +509,25 @@ public JSlider getMaxHashExtractionFileSizeSlider() {
* unlimited.
*
* @return JCheckBox representing the "Unlimited" option for max hash extraction
- * file size.
+ * file size.
*/
public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
return maxHashExtractionFileSizeUnlimitedCheckbox;
}
+ /**
+ * Returns the label that displays the count of differences (diffs) detected.
+ *
+ * @return The JLabel instance showing the diffs count
+ */
+ public JLabel getDiffsCountLabel() {
+ return diffsCountLabel;
+ }
+
{
- // GUI initializer generated by IntelliJ IDEA GUI Designer
- // >>> IMPORTANT!! <<<
- // DO NOT EDIT OR ADD ANY CODE HERE!
+// GUI initializer generated by IntelliJ IDEA GUI Designer
+// >>> IMPORTANT!! <<<
+// DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}
@@ -529,49 +543,29 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
rootPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
tabbedPane = new JTabbedPane();
Font tabbedPaneFont = this.$$$getFont$$$(null, -1, 14, tabbedPane.getFont());
- if (tabbedPaneFont != null)
- tabbedPane.setFont(tabbedPaneFont);
+ if (tabbedPaneFont != null) tabbedPane.setFont(tabbedPaneFont);
tabbedPane.setTabPlacement(1);
tabbedPane.setToolTipText("");
- rootPanel.add(tabbedPane,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_FIXED,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, false));
+ rootPanel.add(tabbedPane, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
homeTab = new JPanel();
homeTab.setLayout(new GridLayoutManager(4, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("\uD83C\uDFE0 Home", homeTab);
newDirectoryButton = new JButton();
newDirectoryButton.setText("New directory");
- homeTab.add(newDirectoryButton,
- new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ homeTab.add(newDirectoryButton, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JSplitPane splitPane1 = new JSplitPane();
- homeTab.add(splitPane1,
- new GridConstraints(1, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ homeTab.add(splitPane1, new GridConstraints(1, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JPanel panel1 = new JPanel();
panel1.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane1.setLeftComponent(panel1);
final JLabel label1 = new JLabel();
Font label1Font = this.$$$getFont$$$(null, Font.BOLD, 16, label1.getFont());
- if (label1Font != null)
- label1.setFont(label1Font);
+ if (label1Font != null) label1.setFont(label1Font);
label1.setText("Most recent scans");
- panel1.add(label1,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane1 = new JScrollPane();
scrollPane1.setToolTipText("Double click a scan to see details");
- panel1.add(scrollPane1,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null,
- 0, false));
+ panel1.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
recentScanTable = new JTable();
recentScanTable.setAutoResizeMode(2);
recentScanTable.setShowHorizontalLines(true);
@@ -581,114 +575,68 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
splitPane1.setRightComponent(panel2);
final JLabel label2 = new JLabel();
Font label2Font = this.$$$getFont$$$(null, Font.BOLD, 16, label2.getFont());
- if (label2Font != null)
- label2.setFont(label2Font);
+ if (label2Font != null) label2.setFont(label2Font);
label2.setText("Monitored directories");
- panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane2 = new JScrollPane();
- panel2.add(scrollPane2,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null,
- 0, false));
- monitoredDirectoryList = new JList();
+ panel2.add(scrollPane2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ monitoredDirectoryList = new JList();
monitoredDirectoryList.setEnabled(true);
monitoredDirectoryList.setSelectionMode(0);
monitoredDirectoryList.setToolTipText("Monitored directories list");
scrollPane2.setViewportView(monitoredDirectoryList);
notificationPanel = new JPanel();
notificationPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 10, 0), -1, -1));
- homeTab.add(notificationPanel,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, true));
+ homeTab.add(notificationPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
final JLabel label3 = new JLabel();
Font label3Font = this.$$$getFont$$$(null, Font.BOLD, 16, label3.getFont());
- if (label3Font != null)
- label3.setFont(label3Font);
+ if (label3Font != null) label3.setFont(label3Font);
label3.setText("Notifications");
- notificationPanel.add(label3,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ notificationPanel.add(label3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
notificationTextArea = new JTextArea();
notificationTextArea.setEditable(false);
- notificationPanel.add(notificationTextArea,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null,
- new Dimension(150, 50), null, 0, false));
+ notificationPanel.add(notificationTextArea, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, new Dimension(150, 50), null, 0, false));
scanTab = new JPanel();
scanTab.setLayout(new GridLayoutManager(10, 4, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("\uD83D\uDD0E Scan", scanTab);
final JLabel label4 = new JLabel();
Font label4Font = this.$$$getFont$$$(null, Font.BOLD, 16, label4.getFont());
- if (label4Font != null)
- label4.setFont(label4Font);
+ if (label4Font != null) label4.setFont(label4Font);
label4.setHorizontalAlignment(0);
label4.setText("Monitored directories");
- scanTab.add(label4,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(label4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JScrollPane scrollPane3 = new JScrollPane();
scrollPane3.setToolTipText("Right click a monitored directory for different actions");
- scanTab.add(scrollPane3,
- new GridConstraints(1, 0, 5, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null,
- 0, false));
+ scanTab.add(scrollPane3, new GridConstraints(1, 0, 5, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
monitoredDirectoriesTable = new JTable();
monitoredDirectoriesTable.setAutoResizeMode(2);
scrollPane3.setViewportView(monitoredDirectoriesTable);
liveFeedContainer = new JScrollPane();
- scanTab.add(liveFeedContainer,
- new GridConstraints(1, 1, 8, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- new Dimension(450, -1), new Dimension(450, -1), null, 0, false));
+ scanTab.add(liveFeedContainer, new GridConstraints(1, 1, 8, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(450, -1), new Dimension(450, -1), null, 0, false));
liveFeedText = new JTextPane();
liveFeedText.setEditable(false);
liveFeedText.setText("");
liveFeedContainer.setViewportView(liveFeedText);
liveFeedTitle = new JLabel();
Font liveFeedTitleFont = this.$$$getFont$$$(null, Font.BOLD, 16, liveFeedTitle.getFont());
- if (liveFeedTitleFont != null)
- liveFeedTitle.setFont(liveFeedTitleFont);
+ if (liveFeedTitleFont != null) liveFeedTitle.setFont(liveFeedTitleFont);
liveFeedTitle.setHorizontalAlignment(0);
liveFeedTitle.setText("Scan logs");
- scanTab.add(liveFeedTitle,
- new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(liveFeedTitle, new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
liveFeedDiffCount = new JLabel();
liveFeedDiffCount.setText("");
- scanTab.add(liveFeedDiffCount,
- new GridConstraints(9, 2, 1, 2, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ scanTab.add(liveFeedDiffCount, new GridConstraints(9, 2, 1, 2, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
scanButton = new JButton();
scanButton.setText("Full scan");
- scanTab.add(scanButton,
- new GridConstraints(6, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(scanButton, new GridConstraints(6, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
clearFeedButton = new JButton();
clearFeedButton.setText("Clear feed");
- scanTab.add(clearFeedButton,
- new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ scanTab.add(clearFeedButton, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
filesTab = new JPanel();
filesTab.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));
- tabbedPane.addTab("📁 Files", filesTab);
+ tabbedPane.addTab("\uD83D\uDDC4\uFE0F Files", filesTab);
final JSplitPane splitPane2 = new JSplitPane();
- filesTab.add(splitPane2,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ filesTab.add(splitPane2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane4 = new JScrollPane();
splitPane2.setLeftComponent(scrollPane4);
filesTable = new JTable();
@@ -698,11 +646,7 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
splitPane2.setRightComponent(panel3);
final JSplitPane splitPane3 = new JSplitPane();
splitPane3.setOrientation(0);
- panel3.add(splitPane3,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ panel3.add(splitPane3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane5 = new JScrollPane();
splitPane3.setLeftComponent(scrollPane5);
fileSummaryTable = new JTable();
@@ -714,47 +658,26 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
scrollPane6.setViewportView(scanSummaryDetails);
final JPanel panel4 = new JPanel();
panel4.setLayout(new GridLayoutManager(5, 2, new Insets(0, 0, 0, 0), -1, -1));
- filesTab.add(panel4,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, true));
+ filesTab.add(panel4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
fileSearchField = new JTextField();
- panel4.add(fileSearchField,
- new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null,
- new Dimension(150, -1), null, 0, false));
+ panel4.add(fileSearchField, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
final JLabel label5 = new JLabel();
label5.setText("Search for file");
- panel4.add(label5, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(label5, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
searchContainingCheckBox = new JCheckBox();
searchContainingCheckBox.setText("File name contains");
- panel4.add(searchContainingCheckBox,
- new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(searchContainingCheckBox, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
descendingCheckBox = new JCheckBox();
descendingCheckBox.setText("Descending");
- panel4.add(descendingCheckBox,
- new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel4.add(descendingCheckBox, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
searchResultCount = new JLabel();
searchResultCount.setText("");
- panel4.add(searchResultCount,
- new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ panel4.add(searchResultCount, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
recentDiffsTab = new JPanel();
- recentDiffsTab.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));
+ recentDiffsTab.setLayout(new GridLayoutManager(3, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("⚠\uFE0F Recent diffs", recentDiffsTab);
final JSplitPane splitPane4 = new JSplitPane();
- recentDiffsTab.add(splitPane4,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ recentDiffsTab.add(splitPane4, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane7 = new JScrollPane();
splitPane4.setLeftComponent(scrollPane7);
diffTable = new JTable();
@@ -767,63 +690,41 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
scrollPane8.setViewportView(diffDetails);
final JLabel label6 = new JLabel();
Font label6Font = this.$$$getFont$$$(null, Font.BOLD, 16, label6.getFont());
- if (label6Font != null)
- label6.setFont(label6Font);
+ if (label6Font != null) label6.setFont(label6Font);
label6.setText("Most recently detected diffs");
- recentDiffsTab.add(label6,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ recentDiffsTab.add(label6, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ diffsCountLabel = new JLabel();
+ diffsCountLabel.setText("Label");
+ recentDiffsTab.add(diffsCountLabel, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
settingsTab = new JPanel();
settingsTab.setLayout(new GridLayoutManager(4, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane.addTab("⚙\uFE0F Settings", settingsTab);
final JSplitPane splitPane5 = new JSplitPane();
- settingsTab.add(splitPane5,
- new GridConstraints(0, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ settingsTab.add(splitPane5, new GridConstraints(0, 0, 4, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JPanel panel5 = new JPanel();
panel5.setLayout(new GridLayoutManager(7, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane5.setLeftComponent(panel5);
final JLabel label7 = new JLabel();
Font label7Font = this.$$$getFont$$$(null, Font.BOLD, 14, label7.getFont());
- if (label7Font != null)
- label7.setFont(label7Font);
+ if (label7Font != null) label7.setFont(label7Font);
label7.setText("Settings");
- panel5.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final Spacer spacer1 = new Spacer();
- panel5.add(spacer1, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER,
- GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
- themePicker = new JComboBox();
- panel5.add(themePicker,
- new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ panel5.add(spacer1, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
+ themePicker = new JComboBox();
+ panel5.add(themePicker, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
restartButton = new JButton();
restartButton.setText("Restart");
- panel5.add(restartButton,
- new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(restartButton, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label8 = new JLabel();
label8.setText("© 2025 PWSS ORG");
- panel5.add(label8, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(label8, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel6 = new JPanel();
panel6.setLayout(new GridLayoutManager(6, 1, new Insets(0, 0, 0, 0), -1, -1));
- panel5.add(panel6,
- new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, false));
+ panel5.add(panel6, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
showSplashScreenCheckBox = new JCheckBox();
showSplashScreenCheckBox.setText("Show splash screen");
- panel6.add(showSplashScreenCheckBox,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(showSplashScreenCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
maxHashExtractionFileSizeSlider = new JSlider();
maxHashExtractionFileSizeSlider.setMajorTickSpacing(5120);
maxHashExtractionFileSizeSlider.setMaximum(102400);
@@ -833,80 +734,49 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
maxHashExtractionFileSizeSlider.setPaintTicks(true);
maxHashExtractionFileSizeSlider.setPaintTrack(true);
maxHashExtractionFileSizeSlider.setSnapToTicks(true);
- panel6.add(maxHashExtractionFileSizeSlider,
- new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ panel6.add(maxHashExtractionFileSizeSlider, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label9 = new JLabel();
label9.setText("Max hash extraction file size (MB)");
- panel6.add(label9, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(label9, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JSeparator separator1 = new JSeparator();
- panel6.add(separator1,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0,
- false));
+ panel6.add(separator1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
maxHashExtractionFileSizeValueLabel = new JLabel();
maxHashExtractionFileSizeValueLabel.setText("Selected:");
- panel6.add(maxHashExtractionFileSizeValueLabel,
- new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ panel6.add(maxHashExtractionFileSizeValueLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
maxHashExtractionFileSizeUnlimitedCheckbox = new JCheckBox();
maxHashExtractionFileSizeUnlimitedCheckbox.setText("Unlimited");
- panel6.add(maxHashExtractionFileSizeUnlimitedCheckbox,
- new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel6.add(maxHashExtractionFileSizeUnlimitedCheckbox, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JLabel label10 = new JLabel();
label10.setText("Theme");
- panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel5.add(label10, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel7 = new JPanel();
panel7.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
splitPane5.setRightComponent(panel7);
final JScrollPane scrollPane9 = new JScrollPane();
- panel7.add(scrollPane9,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, false));
+ panel7.add(scrollPane9, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
quarantineTable = new JTable();
scrollPane9.setViewportView(quarantineTable);
final JLabel label11 = new JLabel();
Font label11Font = this.$$$getFont$$$(null, Font.BOLD, 14, label11.getFont());
- if (label11Font != null)
- label11.setFont(label11Font);
+ if (label11Font != null) label11.setFont(label11Font);
label11.setText("Quarantined files");
- panel7.add(label11, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel7.add(label11, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
scanProgressContainer = new JPanel();
scanProgressContainer.setLayout(new GridLayoutManager(1, 2, new Insets(10, 10, 10, 10), -1, -1));
- rootPanel.add(scanProgressContainer,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_FIXED,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, true));
+ rootPanel.add(scanProgressContainer, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
scanProgress = new JProgressBar();
scanProgress.setIndeterminate(true);
- scanProgressContainer.add(scanProgress,
- new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ scanProgressContainer.add(scanProgress, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
scanProgressLabel = new JLabel();
scanProgressLabel.setText("Scan in progress");
- scanProgressContainer.add(scanProgressLabel,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
- false));
+ scanProgressContainer.add(scanProgressLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
}
/**
* @noinspection ALL
*/
private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
- if (currentFont == null)
- return null;
+ if (currentFont == null) return null;
String resultName;
if (fontName == null) {
resultName = currentFont.getName();
@@ -918,11 +788,9 @@ public JCheckBox getMaxHashExtractionFileSizeUnlimitedCheckbox() {
resultName = currentFont.getName();
}
}
- Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(),
- size >= 0 ? size : currentFont.getSize());
+ Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
boolean isMac = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).startsWith("mac");
- Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize())
- : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize());
+ Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize()) : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize());
return fontWithFallback instanceof FontUIResource ? fontWithFallback : new FontUIResource(fontWithFallback);
}
diff --git a/src/main/java/org/pwss/view/screen/ScanDetailsScreen.form b/src/main/java/org/pwss/view/screen/ScanDetailsScreen.form
index 195d124..b904a3e 100644
--- a/src/main/java/org/pwss/view/screen/ScanDetailsScreen.form
+++ b/src/main/java/org/pwss/view/screen/ScanDetailsScreen.form
@@ -66,7 +66,7 @@
-
+
@@ -78,7 +78,7 @@
-
+
@@ -115,6 +115,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/pwss/view/screen/ScanDetailsScreen.java b/src/main/java/org/pwss/view/screen/ScanDetailsScreen.java
index 9c048b0..9ed37c6 100644
--- a/src/main/java/org/pwss/view/screen/ScanDetailsScreen.java
+++ b/src/main/java/org/pwss/view/screen/ScanDetailsScreen.java
@@ -6,6 +6,7 @@
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JComponent;
+import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
@@ -43,13 +44,10 @@ public class ScanDetailsScreen extends BaseScreen {
* Text pane to show details of the differences (diffs) between scans.
*/
private JTextPane diffDetails;
-
- {
- // GUI initializer generated by IntelliJ IDEA GUI Designer
- // >>> IMPORTANT!! <<<
- // DO NOT EDIT OR ADD ANY CODE HERE!
- $$$setupUI$$$();
- }
+ /**
+ * Label to display the count of differences (diffs) detected.
+ */
+ private JLabel diffsCountLabel;
@Override
public String getScreenName() {
@@ -112,6 +110,22 @@ public JTextPane getScanSummaryDetails() {
return scanSummaryDetails;
}
+ /**
+ * Returns the label that displays the count of differences (diffs) detected.
+ *
+ * @return The JLabel instance showing the diffs count
+ */
+ public JLabel getDiffsCountLabel() {
+ return diffsCountLabel;
+ }
+
+ {
+// GUI initializer generated by IntelliJ IDEA GUI Designer
+// >>> IMPORTANT!! <<<
+// DO NOT EDIT OR ADD ANY CODE HERE!
+ $$$setupUI$$$();
+ }
+
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
@@ -123,20 +137,12 @@ public JTextPane getScanSummaryDetails() {
rootPanel = new JPanel();
rootPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
final JTabbedPane tabbedPane1 = new JTabbedPane();
- rootPanel.add(tabbedPane1,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, false));
+ rootPanel.add(tabbedPane1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
final JPanel panel1 = new JPanel();
panel1.setLayout(new GridLayoutManager(1, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane1.addTab("\uD83D\uDCCB Summaries", null, panel1, "Here you can see file summaries");
final JSplitPane splitPane1 = new JSplitPane();
- panel1.add(splitPane1,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ panel1.add(splitPane1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane1 = new JScrollPane();
splitPane1.setLeftComponent(scrollPane1);
scanSummaryTable = new JTable();
@@ -147,14 +153,10 @@ public JTextPane getScanSummaryDetails() {
scanSummaryDetails.setEditable(false);
scrollPane2.setViewportView(scanSummaryDetails);
final JPanel panel2 = new JPanel();
- panel2.setLayout(new GridLayoutManager(1, 1, new Insets(10, 10, 10, 10), -1, -1));
+ panel2.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));
tabbedPane1.addTab("⚠\uFE0F Diffs detected", null, panel2, "Here you can see the diffs detected for the scan");
final JSplitPane splitPane2 = new JSplitPane();
- panel2.add(splitPane2,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null,
- new Dimension(200, 200), null, 0, false));
+ panel2.add(splitPane2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
final JScrollPane scrollPane3 = new JScrollPane();
splitPane2.setLeftComponent(scrollPane3);
diffTable = new JTable();
@@ -164,19 +166,15 @@ public JTextPane getScanSummaryDetails() {
diffDetails = new JTextPane();
diffDetails.setEditable(false);
scrollPane4.setViewportView(diffDetails);
+ diffsCountLabel = new JLabel();
+ diffsCountLabel.setText("Label");
+ panel2.add(diffsCountLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel3 = new JPanel();
panel3.setLayout(new GridLayoutManager(1, 1, new Insets(0, 10, 10, 10), -1, -1));
- rootPanel.add(panel3,
- new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null,
- 0, true));
+ rootPanel.add(panel3, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, true));
backButton = new JButton();
backButton.setText("Back");
- panel3.add(backButton,
- new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panel3.add(backButton, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
}
/**
From 0079bdf15f1d8b4aaccf92ec0aaca9fff29da181 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Mon, 17 Nov 2025 23:05:53 +0100
Subject: [PATCH 43/44] Added endpoint call for diff count
---
.../org/pwss/controller/HomeController.java | 26 ++++++++++---
.../controller/ScanDetailsController.java | 13 +++++--
.../exception/scan/GetDiffCountException.java | 25 ++++++++++++
.../request/scan/ScanDiffsCountRequest.java | 9 +++++
.../java/org/pwss/service/ScanService.java | 39 +++++++++++++++++++
.../org/pwss/service/network/Endpoint.java | 4 ++
src/main/java/org/pwss/util/ScanUtil.java | 26 +++++++++++++
7 files changed, 133 insertions(+), 9 deletions(-)
create mode 100644 src/main/java/org/pwss/exception/scan/GetDiffCountException.java
create mode 100644 src/main/java/org/pwss/model/request/scan/ScanDiffsCountRequest.java
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 3d1b4c8..70757ad 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -29,6 +29,7 @@
import org.pwss.exception.metadata.MetadataKeyNameRetrievalException;
import org.pwss.exception.monitored_directory.MonitoredDirectoryGetAllException;
import org.pwss.exception.scan.GetAllMostRecentScansException;
+import org.pwss.exception.scan.GetDiffCountException;
import org.pwss.exception.scan.GetMostRecentScansException;
import org.pwss.exception.scan.GetScanDiffsException;
import org.pwss.exception.scan.LiveFeedException;
@@ -131,6 +132,11 @@ public final class HomeController extends BaseController {
*/
private List recentScans;
+ /**
+ * Count of differences found in recent scans.
+ */
+ private int recentDiffsCount;
+
/**
* List of most recent differences detected in the scans.
*/
@@ -263,8 +269,17 @@ void fetchDataAndRefreshView() {
if (recentScans.isEmpty()) {
recentDiffs = List.of();
} else {
+ List distinctRecentScans = ScanUtil.getScansDistinctByDirectory(recentScans);
+
+ // Calculate diff count for the recent scans
+ recentDiffsCount = 0;
+ for (Scan scan : distinctRecentScans) {
+ int count = scanService.getScanDiffsCount(scan.id());
+ recentDiffsCount += count;
+ }
+
// Fetch diffs for the most recent scan to show in the diffs table
- recentDiffs = recentScans.stream()
+ recentDiffs = distinctRecentScans.stream()
.flatMap(scan -> safeGetDiffs(scan.id()).stream())
.toList();
}
@@ -282,8 +297,8 @@ void fetchDataAndRefreshView() {
scanRunning = true;
startPollingScanLiveFeed(false, Collections.emptyList());
}
- } catch (MonitoredDirectoryGetAllException | ExecutionException | InterruptedException | JsonProcessingException
- | GetAllMostRecentScansException | ScanStatusException e) {
+ } catch (MonitoredDirectoryGetAllException | ExecutionException | InterruptedException |
+ JsonProcessingException | GetAllMostRecentScansException | ScanStatusException | GetDiffCountException e) {
log.error("Error getting data: {}", e.getMessage());
SwingUtilities.invokeLater(() -> screen.showError("Error getting data"));
}
@@ -535,8 +550,7 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
screen.getMonitoredDirectoriesTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Update diffs count label
- // TODO: Update with count from endpoint when available
- screen.getDiffsCountLabel().setText("Diffs found: " + (recentDiffs != null ? recentDiffs.size() : 0));
+ screen.getDiffsCountLabel().setText("Diffs found: " + recentDiffsCount);
DiffTableModel diffTableModel = new DiffTableModel(recentDiffs != null ? recentDiffs : List.of());
screen.getDiffTable().setModel(diffTableModel);
@@ -879,7 +893,7 @@ private void startPollingScanLiveFeed(boolean singleDirectory, List imp
/**
* Logger for logging messages within this controller.
*/
- private final org.slf4j.Logger log = LoggerFactory.getLogger(LoginController.class);
+ private final org.slf4j.Logger log = LoggerFactory.getLogger(ScanDetailsController.class);
/**
* Service responsible for managing scan summaries.
@@ -49,12 +49,18 @@ public class ScanDetailsController extends BaseController imp
* List of scan summaries. This can be empty if no summaries are available.
*/
private List scanSummaries;
+
/**
* List of differences (diffs) between scans. This can be empty if no diffs are
* available.
*/
private List diffs;
+ /**
+ * Count of differences found. This can be null if the count is not available.
+ */
+ private Integer diffCount = 0;
+
/**
* Constructs a ScanDetailsController with the given screen and initializes
* services and lists.
@@ -97,6 +103,8 @@ private void fetchData() {
if (scanId != null) {
// Fetch scan summaries for the scan
scanSummaries = scanSummaryService.getScanSummaryForScan(scanId);
+ // Fetch diff count for the scan
+ diffCount = scanService.getScanDiffsCount(scanId);
// Fetch diffs for the scan
diffs = scanService.getDiffs(scanId, 1000, null, true);
}
@@ -142,8 +150,7 @@ void refreshView() {
screen.getScanSummaryTable().setModel(simpleSummaryTableModel);
// Update diffs count label
- // TODO: Update with count from endpoint when available
- screen.getDiffsCountLabel().setText("Diffs found: " + diffs.size());
+ screen.getDiffsCountLabel().setText("Diffs found: " + diffCount);
// Populate diffs table
DiffTableModel diffTableModel = new DiffTableModel(diffs);
diff --git a/src/main/java/org/pwss/exception/scan/GetDiffCountException.java b/src/main/java/org/pwss/exception/scan/GetDiffCountException.java
new file mode 100644
index 0000000..9030fd9
--- /dev/null
+++ b/src/main/java/org/pwss/exception/scan/GetDiffCountException.java
@@ -0,0 +1,25 @@
+package org.pwss.exception.scan;
+
+import java.io.Serial;
+import org.pwss.exception.PWSSbaseException;
+
+/**
+ * Exception thrown when there is an error while getting the difference count
+ * during a scan operation.
+ */
+public class GetDiffCountException extends PWSSbaseException {
+ /**
+ * Serial version UID for serialization.
+ */
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new GetDiffCountException with the specified detail message.
+ *
+ * @param message the detail message.
+ */
+ public GetDiffCountException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/pwss/model/request/scan/ScanDiffsCountRequest.java b/src/main/java/org/pwss/model/request/scan/ScanDiffsCountRequest.java
new file mode 100644
index 0000000..cca6fef
--- /dev/null
+++ b/src/main/java/org/pwss/model/request/scan/ScanDiffsCountRequest.java
@@ -0,0 +1,9 @@
+package org.pwss.model.request.scan;
+
+/**
+ * Represents a request to count differences in a scan.
+ *
+ * @param scanId the identifier for the scan to count differences for
+ */
+public record ScanDiffsCountRequest(long scanId) {
+}
diff --git a/src/main/java/org/pwss/service/ScanService.java b/src/main/java/org/pwss/service/ScanService.java
index 33698db..6f4d6b8 100644
--- a/src/main/java/org/pwss/service/ScanService.java
+++ b/src/main/java/org/pwss/service/ScanService.java
@@ -7,6 +7,7 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.pwss.exception.scan.GetAllMostRecentScansException;
+import org.pwss.exception.scan.GetDiffCountException;
import org.pwss.exception.scan.GetMostRecentScansException;
import org.pwss.exception.scan.GetScanDiffsException;
import org.pwss.exception.scan.LiveFeedException;
@@ -18,6 +19,7 @@
import org.pwss.model.entity.Scan;
import org.pwss.model.request.scan.GetMostRecentScansRequest;
import org.pwss.model.request.scan.GetScanDiffsRequest;
+import org.pwss.model.request.scan.ScanDiffsCountRequest;
import org.pwss.model.request.scan.StartScanAllRequest;
import org.pwss.model.request.scan.StartSingleScanRequest;
import org.pwss.model.response.LiveFeedResponse;
@@ -200,6 +202,19 @@ public List getMostRecentScansAll() throws GetAllMostRecentScansException,
};
}
+ /**
+ * Retrieves the diffs for a specific scan by sending a request to the SCAN_DIFFS endpoint.
+ *
+ * @param scanId The ID of the scan to retrieve diffs for.
+ * @param limit The maximum number of diffs to retrieve.
+ * @param sortField The field by which to sort the diffs.
+ * @param ascending Whether to sort the diffs in ascending order.
+ * @return A list of Diff objects if the request is successful.
+ * @throws GetScanDiffsException If the attempt to retrieve the scan diffs fails due to various reasons such as invalid credentials or server error.
+ * @throws ExecutionException If an error occurs during the asynchronous execution of the request.
+ * @throws InterruptedException If the thread executing the request is interrupted.
+ * @throws JsonProcessingException If an error occurs while processing JSON data.
+ */
public List getDiffs(long scanId, long limit, String sortField, boolean ascending) throws GetScanDiffsException, ExecutionException, InterruptedException, JsonProcessingException {
String body = objectMapper.writeValueAsString(new GetScanDiffsRequest(scanId, limit, sortField, ascending));
HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.SCAN_DIFFS, body);
@@ -212,4 +227,28 @@ public List getDiffs(long scanId, long limit, String sortField, boolean as
default -> Collections.emptyList();
};
}
+
+ /**
+ * Retrieves the count of diffs for a specific scan by sending a request to the DIFF_COUNT endpoint.
+ *
+ * @param scanId The ID of the scan to retrieve the diff count for.
+ * @return The count of diffs for the specified scan if the request is successful.
+ * @throws GetDiffCountException If the attempt to retrieve the scan diffs count fails due to various reasons such as invalid credentials, scan not found, or server error.
+ * @throws ExecutionException If an error occurs during the asynchronous execution of the request.
+ * @throws InterruptedException If the thread executing the request is interrupted.
+ * @throws JsonProcessingException If an error occurs while processing JSON data.
+ */
+ public Integer getScanDiffsCount(long scanId) throws JsonProcessingException, ExecutionException, InterruptedException, GetDiffCountException {
+ String body = objectMapper.writeValueAsString(new ScanDiffsCountRequest(scanId));
+ HttpResponse response = PwssHttpClient.getInstance().request(Endpoint.DIFF_COUNT, body);
+
+ return switch (response.statusCode()) {
+ case 200 -> Integer.parseInt(response.body());
+ case 400 -> throw new GetDiffCountException("Get scan diffs count failed: Bad request.");
+ case 401 -> throw new GetDiffCountException("Get scan diffs count failed: User not authorized to perform this action.");
+ case 404 -> throw new GetDiffCountException("Get scan diffs count failed: Scan with the given ID not found.");
+ case 500 -> throw new GetDiffCountException("Get scan diffs count failed: An error occurred on the server while attempting to get the diff count.");
+ default -> null;
+ };
+ }
}
diff --git a/src/main/java/org/pwss/service/network/Endpoint.java b/src/main/java/org/pwss/service/network/Endpoint.java
index a02c421..a03c96a 100644
--- a/src/main/java/org/pwss/service/network/Endpoint.java
+++ b/src/main/java/org/pwss/service/network/Endpoint.java
@@ -54,6 +54,10 @@ public enum Endpoint {
* Endpoint for retrieving diffs for a given scan.
*/
SCAN_DIFFS(HTTP_Method.POST, A.BASE_URL + A.SCAN + "diff"),
+ /**
+ * Endpoint for retrieving the count of diffs for a given scan.
+ */
+ DIFF_COUNT(HTTP_Method.POST, A.BASE_URL + A.SCAN + "diff/count"),
// Directory Endpoints
diff --git a/src/main/java/org/pwss/util/ScanUtil.java b/src/main/java/org/pwss/util/ScanUtil.java
index b93cebb..4811055 100644
--- a/src/main/java/org/pwss/util/ScanUtil.java
+++ b/src/main/java/org/pwss/util/ScanUtil.java
@@ -1,5 +1,12 @@
package org.pwss.util;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import org.pwss.model.entity.Scan;
+
/**
* Utility class for constructing scan-related messages.
*/
@@ -34,4 +41,23 @@ private ScanUtil() {
public static String constructDiffMessageString(long diffNumber) {
return SCAN_COMPLETED_DIFFS_PREFIX + diffNumber + SCAN_COMPLETED_DIFFS_SUFFIX;
}
+
+ /**
+ * Filters the provided list of scans to return a list containing only distinct
+ * scans based on their monitored directory IDs.
+ *
+ * @param scans The list of scans to filter.
+ * @return A list of scans with distinct monitored directory IDs.
+ */
+ public static List getScansDistinctByDirectory(List scans) {
+ return scans.stream()
+ .filter(distinctByKey(scan -> scan.monitoredDirectory().id()))
+ .toList();
+ }
+
+ // Helper method to create a predicate for distinct filtering based on a key extractor
+ private static Predicate distinctByKey(Function super T, ?> keyExtractor) {
+ Set seen = ConcurrentHashMap.newKeySet();
+ return t -> seen.add(keyExtractor.apply(t));
+ }
}
From d5edc68d1b9e99f25757ee66c0b48657a3d03199 Mon Sep 17 00:00:00 2001
From: pwgit-create
Date: Mon, 17 Nov 2025 23:40:45 +0100
Subject: [PATCH 44/44] Set limit to Integer.MAX_VALUE -1
---
src/main/java/org/pwss/controller/HomeController.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/pwss/controller/HomeController.java b/src/main/java/org/pwss/controller/HomeController.java
index 70757ad..61d761c 100644
--- a/src/main/java/org/pwss/controller/HomeController.java
+++ b/src/main/java/org/pwss/controller/HomeController.java
@@ -316,7 +316,7 @@ void fetchDataAndRefreshView() {
*/
private List safeGetDiffs(long scanId) {
try {
- return scanService.getDiffs(scanId, 1000, null, false);
+ return scanService.getDiffs(scanId, (Integer.MAX_VALUE -1), null, false);
} catch (GetScanDiffsException | ExecutionException | InterruptedException | JsonProcessingException e) {
return List.of();
}