diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6df9e54 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "TranslationLoaderEnum", + "request": "launch", + "mainClass": "com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum", + "projectName": "BackupManager" + }, + { + "type": "java", + "name": "MainApp", + "request": "launch", + "mainClass": "com.mycompany.autobackupprogram.MainApp", + "projectName": "BackupManager" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index cd8e015..bd47a24 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,18 @@ Each backup is carefully saved, and the program maintains a detailed log of all completed operations. Users can also view, manage, and edit the details of each backup, ensuring complete control and customization over saved data. This tool is an ideal solution for efficiently and securely protecting files, minimizing the risk of data loss. -## Screenshots +## Screenshots and Videos +![](./docs/imgs/BackupManagerPresentation.gif) + +![](./docs/imgs/AutoBackup.png) +![](./docs/imgs/BackupList.png) +![](./docs/imgs/CompletedBackup.png) +![](./docs/imgs/ThemeLanguage.png) + +### Multi theme +![](./docs/imgs/Home.png) +![](./docs/imgs/Home2.png) +![](./docs/imgs/Home3.png) ## Startup Logic diff --git a/docs/imgs/AutoBackup.png b/docs/imgs/AutoBackup.png new file mode 100644 index 0000000..b4c3542 Binary files /dev/null and b/docs/imgs/AutoBackup.png differ diff --git a/docs/imgs/BackupList.png b/docs/imgs/BackupList.png new file mode 100644 index 0000000..7c8738a Binary files /dev/null and b/docs/imgs/BackupList.png differ diff --git a/docs/imgs/BackupManagerPresentation.gif b/docs/imgs/BackupManagerPresentation.gif new file mode 100644 index 0000000..7d9ec62 Binary files /dev/null and b/docs/imgs/BackupManagerPresentation.gif differ diff --git a/docs/imgs/CompletedBackup.png b/docs/imgs/CompletedBackup.png new file mode 100644 index 0000000..f99fb75 Binary files /dev/null and b/docs/imgs/CompletedBackup.png differ diff --git a/docs/imgs/Home.png b/docs/imgs/Home.png new file mode 100644 index 0000000..8f6f547 Binary files /dev/null and b/docs/imgs/Home.png differ diff --git a/docs/imgs/Home2.png b/docs/imgs/Home2.png new file mode 100644 index 0000000..c5a6d2a Binary files /dev/null and b/docs/imgs/Home2.png differ diff --git a/docs/imgs/Home3.png b/docs/imgs/Home3.png new file mode 100644 index 0000000..bec83a5 Binary files /dev/null and b/docs/imgs/Home3.png differ diff --git a/docs/imgs/ThemeLanguage.png b/docs/imgs/ThemeLanguage.png new file mode 100644 index 0000000..eb4a144 Binary files /dev/null and b/docs/imgs/ThemeLanguage.png differ diff --git a/src/main/java/com/mycompany/autobackupprogram/BackupOperations.java b/src/main/java/com/mycompany/autobackupprogram/BackupOperations.java index 347ce5e..979126c 100644 --- a/src/main/java/com/mycompany/autobackupprogram/BackupOperations.java +++ b/src/main/java/com/mycompany/autobackupprogram/BackupOperations.java @@ -7,6 +7,7 @@ import java.awt.TrayIcon; import java.io.File; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileVisitResult; @@ -19,6 +20,8 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -28,15 +31,15 @@ import javax.swing.SwingUtilities; import com.mycompany.autobackupprogram.Entities.Backup; -import com.mycompany.autobackupprogram.Enums.ConfigKey; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationCategory; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationKey; +import com.mycompany.autobackupprogram.Entities.Preferences; import com.mycompany.autobackupprogram.GUI.BackupManagerGUI; import com.mycompany.autobackupprogram.GUI.BackupProgressGUI; import com.mycompany.autobackupprogram.Entities.TimeInterval; import com.mycompany.autobackupprogram.Logger.LogLevel; -public class BackupOperations{ +public class BackupOperations { private static final JSONAutoBackup JSON = new JSONAutoBackup(); private static Thread zipThread; @@ -58,13 +61,15 @@ public static void SingleBackup(Backup backup, TrayIcon trayIcon, BackupProgress LocalDateTime dateNow = LocalDateTime.now(); String date = dateNow.format(dateForfolderNameFormatter); - String name1 = path1.substring(path1.length() - 1); + String name1 = path1.substring(path1.length()-1, path1.length()-1); for(int i = path1.length() - 1; i >= 0; i--) { if(path1.charAt(i) != temp.charAt(0)) name1 = path1.charAt(i) + name1; else break; } + name1 = removeExtension(name1); + path2 = path2 + "\\" + name1 + " (Backup " + date + ")"; zipDirectory(path1, path2+".zip", backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); @@ -80,6 +85,14 @@ public static void SingleBackup(Backup backup, TrayIcon trayIcon, BackupProgress if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); } } + + public static String removeExtension(String fileName) { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0) { + return fileName.substring(0, dotIndex); + } + return fileName; + } private static void updateAfterBackup(String path1, String path2, Backup backup, TrayIcon trayIcon, JButton singleBackupBtn, JToggleButton autoBackupBtn) { if (backup == null) throw new IllegalArgumentException("Backup cannot be null!"); @@ -106,7 +119,7 @@ private static void updateAfterBackup(String path1, String path2, Backup backup, backup.setBackupCount(backup.getBackupCount()+1); try { - List backups = JSON.ReadBackupListFromJSON(ConfigKey.BACKUP_FILE_STRING.getValue(), ConfigKey.RES_DIRECTORY_STRING.getValue()); + List backups = JSON.ReadBackupListFromJSON(Preferences.getBackupList().getDirectory(), Preferences.getBackupList().getFile()); for (Backup b : backups) { if (b.getBackupName().equals(backup.getBackupName())) { @@ -170,89 +183,95 @@ public static boolean CheckInputCorrect(String backupName, String path1, String public static void zipDirectory(String sourceDirectoryPath, String targetZipPath, Backup backup, TrayIcon trayIcon, BackupProgressGUI progressBar, JButton singleBackupBtn, JToggleButton autoBackupBtn) throws IOException { // Track copied files Logger.logMessage("Starting zipping process", LogLevel.INFO); - File file = new File(sourceDirectoryPath); + File file = new File(sourceDirectoryPath.trim()); + int totalFilesCount = file.isDirectory() ? countFilesInDirectory(file) : 1; - - AtomicInteger copiedFilesCount = new AtomicInteger(0); // Track copied files + AtomicInteger copiedFilesCount = new AtomicInteger(0); zipThread = new Thread(() -> { Path sourceDir = Paths.get(sourceDirectoryPath); String rootFolderName = sourceDir.getFileName().toString(); // Get the root folder name - try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(targetZipPath))) { - if (file.isFile()) { - addFileToZip(sourceDirectoryPath, zipOut, file.toPath(), "", copiedFilesCount, totalFilesCount, backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); - } else { - Files.walkFileTree(sourceDir, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (Thread.currentThread().isInterrupted()) { - Logger.logMessage("Zipping process manually interrupted", Logger.LogLevel.INFO); - if (singleBackupBtn != null) singleBackupBtn.setEnabled(true); - if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); - return FileVisitResult.TERMINATE; // Stop if interrupted - } + try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(targetZipPath))) { + if (file.isFile()) { + addFileToZip(sourceDirectoryPath, targetZipPath, zipOut, file.toPath(), file.getName(), copiedFilesCount, totalFilesCount, backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); + } else { + Files.walkFileTree(sourceDir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (Thread.currentThread().isInterrupted()) { + Logger.logMessage("Zipping process manually interrupted", Logger.LogLevel.INFO); + if (singleBackupBtn != null) singleBackupBtn.setEnabled(true); + if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); + return FileVisitResult.TERMINATE; // Stop if interrupted + } - // Calculate the relative path inside the zip - Path targetFilePath = sourceDir.relativize(file); - String zipEntryName = rootFolderName + "/" + targetFilePath.toString(); + // Calculate the relative path inside the zip + Path targetFilePath = sourceDir.relativize(file); + String zipEntryName = rootFolderName + "/" + targetFilePath.toString(); - // Create a new zip entry for the file - zipOut.putNextEntry(new ZipEntry(zipEntryName)); + // Create a new zip entry for the file + zipOut.putNextEntry(new ZipEntry(zipEntryName)); - // Copy the file content to the zip output stream - try (InputStream in = Files.newInputStream(file)) { - byte[] buffer = new byte[1024]; - int len; - while ((len = in.read(buffer)) > 0) { - zipOut.write(buffer, 0, len); - } + // Copy the file content to the zip output stream + try (InputStream in = Files.newInputStream(file)) { + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) > 0) { + zipOut.write(buffer, 0, len); } - - zipOut.closeEntry(); // Close the zip entry after the file is written - - // Update progress - int filesCopiedSoFar = copiedFilesCount.incrementAndGet(); - int actualProgress = (int) (((double) filesCopiedSoFar / totalFilesCount) * 100); - UpdateProgressPercentage(actualProgress, sourceDirectoryPath, targetZipPath, backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); // Update progress percentage - - return FileVisitResult.CONTINUE; } - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (Thread.currentThread().isInterrupted()) { - Logger.logMessage("Zipping process manually interrupted", Logger.LogLevel.INFO); - if (singleBackupBtn != null) singleBackupBtn.setEnabled(true); - if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); - return FileVisitResult.TERMINATE; // Stop if interrupted - } + zipOut.closeEntry(); // Close the zip entry after the file is written + + // Update progress + int filesCopiedSoFar = copiedFilesCount.incrementAndGet(); + int actualProgress = (int) (((double) filesCopiedSoFar / totalFilesCount) * 100); + UpdateProgressPercentage(actualProgress, sourceDirectoryPath, targetZipPath, backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); // Update progress percentage + + return FileVisitResult.CONTINUE; + } - // Create directory entry in the zip if needed - Path targetDir = sourceDir.relativize(dir); - zipOut.putNextEntry(new ZipEntry(rootFolderName + "/" + targetDir.toString() + "/")); - zipOut.closeEntry(); - return FileVisitResult.CONTINUE; + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (Thread.currentThread().isInterrupted()) { + Logger.logMessage("Zipping process manually interrupted", Logger.LogLevel.INFO); + if (singleBackupBtn != null) singleBackupBtn.setEnabled(true); + if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); + return FileVisitResult.TERMINATE; // Stop if interrupted } - }); - } - } catch (IOException ex) { - Logger.logMessage("An error occurred: " + ex.getMessage() , Logger.LogLevel.ERROR, ex); - if (singleBackupBtn != null) singleBackupBtn.setEnabled(true); - if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); + + // case when the initial folder is empty + if (totalFilesCount == 0) { + UpdateProgressPercentage(100, sourceDirectoryPath, targetZipPath, backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); + return FileVisitResult.TERMINATE; + } + + // Create directory entry in the zip if needed + Path targetDir = sourceDir.relativize(dir); + zipOut.putNextEntry(new ZipEntry(rootFolderName + "/" + targetDir.toString() + "/")); + zipOut.closeEntry(); + return FileVisitResult.CONTINUE; + } + }); } - }); + } catch (Exception ex) { + Logger.logMessage("An error occurred: " + ex.getMessage() , Logger.LogLevel.ERROR, ex); + ex.printStackTrace(); + } finally { + if (singleBackupBtn != null) singleBackupBtn.setEnabled(true); + if (autoBackupBtn != null) autoBackupBtn.setEnabled(true); + } + }); - zipThread.start(); // Start the zipping thread - } + zipThread.start(); // Start the zipping thread + } - private static void addFileToZip(String sourceDirectoryPath, ZipOutputStream zipOut, Path file, String zipEntryName, AtomicInteger copiedFilesCount, int totalFilesCount, Backup backup, TrayIcon trayIcon, BackupProgressGUI progressBar, JButton singleBackupBtn, JToggleButton autoBackupBtn) throws IOException { - if (zipEntryName.isEmpty()) { + private static void addFileToZip(String sourceDirectoryPath, String destinationDirectoryPath, ZipOutputStream zipOut, Path file, String zipEntryName, AtomicInteger copiedFilesCount, int totalFilesCount, Backup backup, TrayIcon trayIcon, BackupProgressGUI progressBar, JButton singleBackupBtn, JToggleButton autoBackupBtn) throws IOException { + if (zipEntryName == null || zipEntryName.isEmpty()) { zipEntryName = file.getFileName().toString(); - } - + } zipOut.putNextEntry(new ZipEntry(zipEntryName)); - try (InputStream in = Files.newInputStream(file)) { byte[] buffer = new byte[1024]; int len; @@ -260,18 +279,17 @@ private static void addFileToZip(String sourceDirectoryPath, ZipOutputStream zip zipOut.write(buffer, 0, len); } } - zipOut.closeEntry(); int filesCopiedSoFar = copiedFilesCount.incrementAndGet(); int actualProgress = (int) (((double) filesCopiedSoFar / totalFilesCount) * 100); - UpdateProgressPercentage(actualProgress, sourceDirectoryPath, zipOut.toString(), backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); + UpdateProgressPercentage(actualProgress, sourceDirectoryPath, destinationDirectoryPath, backup, trayIcon, progressBar, singleBackupBtn, autoBackupBtn); } public static void updateBackupList(List backups) { if (backups == null) throw new IllegalArgumentException("Backup list is null!"); - JSON.UpdateBackupListJSON(ConfigKey.BACKUP_FILE_STRING.getValue(), ConfigKey.RES_DIRECTORY_STRING.getValue(), backups); + JSON.UpdateBackupListJSON(Preferences.getBackupList().getDirectory(), Preferences.getBackupList().getFile(), backups); if (BackupManagerGUI.model != null) updateTableWithNewBackupList(backups); @@ -280,14 +298,13 @@ public static void updateBackupList(List backups) { public static void updateBackup(List backups, Backup updatedBackup) { if (updatedBackup == null) throw new IllegalArgumentException("Backup is null!"); - JSON.UpdateSingleBackupInJSON(ConfigKey.BACKUP_FILE_STRING.getValue(), ConfigKey.RES_DIRECTORY_STRING.getValue(), updatedBackup); + JSON.UpdateSingleBackupInJSON(Preferences.getBackupList().getDirectory(), Preferences.getBackupList().getFile(), updatedBackup); if (BackupManagerGUI.model != null) updateTableWithNewBackupList(backups); } public static void UpdateProgressPercentage(int value, String path1, String path2, Backup backup, TrayIcon trayIcon, BackupProgressGUI progressBar, JButton singleBackupBtn, JToggleButton autoBackupBtn) { - if (value == 0 || value == 25 || value == 50 || value == 75 || value == 100) Logger.logMessage("Progress: " + value, Logger.LogLevel.INFO); @@ -296,7 +313,77 @@ public static void UpdateProgressPercentage(int value, String path1, String path if (value == 100) { updateAfterBackup(path1, path2, backup, trayIcon, singleBackupBtn, autoBackupBtn); + + // delete + deleteOldBackupsIfNecessary(backup.getMaxBackupsToKeep(), path2); + } + } + + private static void deleteOldBackupsIfNecessary(int maxBackupsToKeep, String destinationPath) { + Logger.logMessage("Deleting old backups if necessary", LogLevel.INFO); + + File folder = new File(destinationPath).getParentFile(); + String fileBackuppedToSearch = new File(destinationPath).getName(); + + // extract the file name (before the parentesis) + String baseName = fileBackuppedToSearch.substring(0, fileBackuppedToSearch.indexOf(" (Backup")); + + if (folder != null && folder.isDirectory()) { + // get current count + FilenameFilter filter = (dir, name) -> name.matches(baseName + " \\(Backup \\d{2}-\\d{2}-\\d{4} \\d{2}\\.\\d{2}\\.\\d{2}\\)\\.zip"); + File[] matchingFiles = folder.listFiles(filter); // getting files for that filter + + if (matchingFiles == null) { + Logger.logMessage("Error during deleting old backups: none matching files", LogLevel.WARN); + return; + } + + // check if the max is passed, and if it is, remove the oldest + if (matchingFiles.length > maxBackupsToKeep) { + Logger.logMessage("Found " + matchingFiles.length + " matching files, exceeding max allowed: " + maxBackupsToKeep, LogLevel.INFO); + + Arrays.sort(matchingFiles, (f1, f2) -> { + String datePattern = "\\(Backup (\\d{2}-\\d{2}-\\d{4} \\d{2}\\.\\d{2}\\.\\d{2})\\)\\.zip"; // regex aggiornata + + try { + // extracting dates from file names + String date1 = extractDateFromFileName(f1.getName(), datePattern); + String date2 = extractDateFromFileName(f2.getName(), datePattern); + + LocalDateTime dateTime1 = LocalDateTime.parse(date1, BackupManagerGUI.dateForfolderNameFormatter); + LocalDateTime dateTime2 = LocalDateTime.parse(date2, BackupManagerGUI.dateForfolderNameFormatter); + + return dateTime1.compareTo(dateTime2); + } catch (Exception e) { + Logger.logMessage("Error parsing dates: " + e.getMessage(), LogLevel.ERROR); + return 0; + } + }); + + // delete older files + for (int i = 0; i < matchingFiles.length - maxBackupsToKeep; i++) { + File fileToDelete = matchingFiles[i]; + if (fileToDelete.delete()) { + Logger.logMessage("Deleted old backup: " + fileToDelete.getName(), LogLevel.INFO); + } else { + Logger.logMessage("Failed to delete old backup: " + fileToDelete.getName(), LogLevel.WARN); + } + } + } + } else { + Logger.logMessage("Destination path is not a directory: " + destinationPath, LogLevel.ERROR); + } + } + + private static String extractDateFromFileName(String fileName, String pattern) throws Exception { + Pattern regex = Pattern.compile(pattern); + Matcher matcher = regex.matcher(fileName); + + if (matcher.find()) { + return matcher.group(1); } + + throw new Exception("No date found in file name: " + fileName); } public static void updateTableWithNewBackupList(List updatedBackups) { diff --git a/src/main/java/com/mycompany/autobackupprogram/BackupService.java b/src/main/java/com/mycompany/autobackupprogram/BackupService.java index 6c1d00a..7c3650d 100644 --- a/src/main/java/com/mycompany/autobackupprogram/BackupService.java +++ b/src/main/java/com/mycompany/autobackupprogram/BackupService.java @@ -11,9 +11,11 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; + import javax.swing.JFrame; import com.mycompany.autobackupprogram.Entities.Backup; +import com.mycompany.autobackupprogram.Entities.Preferences; import com.mycompany.autobackupprogram.Enums.ConfigKey; import com.mycompany.autobackupprogram.GUI.BackupManagerGUI; @@ -112,7 +114,7 @@ class BackupTask implements Runnable { public void run() { Logger.logMessage("Checking for automatic backup...", Logger.LogLevel.INFO); try { - List backups = json.ReadBackupListFromJSON(ConfigKey.BACKUP_FILE_STRING.getValue(), ConfigKey.RES_DIRECTORY_STRING.getValue()); + List backups = json.ReadBackupListFromJSON(Preferences.getBackupList().getDirectory(), Preferences.getBackupList().getFile()); List needsBackup = getBackupsToDo(backups); if (needsBackup != null && !needsBackup.isEmpty()) { Logger.logMessage("Start backup process.", Logger.LogLevel.INFO); @@ -122,7 +124,6 @@ public void run() { } } catch (IOException ex) { Logger.logMessage("An error occurred: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); - ex.printStackTrace(); } } diff --git a/src/main/java/com/mycompany/autobackupprogram/Dialogs/PreferencesDialog.java b/src/main/java/com/mycompany/autobackupprogram/Dialogs/PreferencesDialog.java index c74aae7..c7528ca 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Dialogs/PreferencesDialog.java +++ b/src/main/java/com/mycompany/autobackupprogram/Dialogs/PreferencesDialog.java @@ -1,6 +1,7 @@ package com.mycompany.autobackupprogram.Dialogs; -import com.formdev.flatlaf.FlatIntelliJLaf; +import com.mycompany.autobackupprogram.Logger; +import com.mycompany.autobackupprogram.Logger.LogLevel; import com.mycompany.autobackupprogram.Entities.Preferences; import com.mycompany.autobackupprogram.Enums.ConfigKey; import com.mycompany.autobackupprogram.Enums.LanguagesEnum; @@ -9,23 +10,26 @@ import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationCategory; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationKey; import com.mycompany.autobackupprogram.Managers.ThemeManager; - import static com.mycompany.autobackupprogram.GUI.BackupManagerGUI.OpenExceptionMessage; + import java.awt.Image; import java.io.IOException; import java.util.Arrays; + import javax.swing.ImageIcon; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; import org.json.simple.parser.ParseException; +import com.mycompany.autobackupprogram.GUI.BackupManagerGUI; + public class PreferencesDialog extends javax.swing.JDialog { - private boolean isApply = false; + private final BackupManagerGUI mainGui; - public PreferencesDialog(java.awt.Frame parent, boolean modal) { + public PreferencesDialog(java.awt.Frame parent, boolean modal, BackupManagerGUI mainGui) { super(parent, modal); + this.mainGui = mainGui; + initComponents(); // logo application @@ -37,10 +41,6 @@ public PreferencesDialog(java.awt.Frame parent, boolean modal) { setThemes(); setTranslations(); } - - public void changeTheme() { - - } /** * This method is called from within the constructor to initialize the form. @@ -136,18 +136,27 @@ private void themesComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GE }//GEN-LAST:event_themesComboBoxActionPerformed private void applyBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyBtnActionPerformed - isApply = true; + String selectedLanguage = (String) languagesComboBox.getSelectedItem(); + String selectedTheme = (String) themesComboBox.getSelectedItem(); + + Logger.logMessage("Updating preferences -> Language: " + selectedLanguage + "; Theme: " + selectedTheme, LogLevel.INFO); + try { // translactions - Preferences.setLanguage((String) languagesComboBox.getSelectedItem()); + Preferences.setLanguage(selectedLanguage); TranslationLoaderEnum.loadTranslations(ConfigKey.LANGUAGES_DIRECTORY_STRING.getValue() + Preferences.getLanguage().getFileName()); setTranslations(); // theme - Preferences.setTheme((String) themesComboBox.getSelectedItem()); + Preferences.setTheme(selectedTheme); ThemeManager.updateThemeDialog(this); - } catch (IOException | ParseException e) { - e.printStackTrace(); + + // update globally + Preferences.updatePreferencesToJSON(); + mainGui.reloadPreferences(); + } catch (IOException | ParseException ex) { + Logger.logMessage("An error occurred during applying preferences: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } }//GEN-LAST:event_applyBtnActionPerformed @@ -187,10 +196,6 @@ private void setTranslations() { jLabel1.setText(TranslationCategory.PREFERENCES_DIALOG.getTranslation(TranslationKey.LANGUAGE)); jLabel2.setText(TranslationCategory.PREFERENCES_DIALOG.getTranslation(TranslationKey.THEME)); } - - public boolean isApply() { - return isApply; - } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton applyBtn; diff --git a/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.form b/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.form index f503b18..7ee92e1 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.form +++ b/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.form @@ -37,9 +37,9 @@ - + - + @@ -88,12 +88,12 @@ - + - + diff --git a/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.java b/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.java index 1dd39ef..7c848ee 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.java +++ b/src/main/java/com/mycompany/autobackupprogram/Dialogs/TimePicker.java @@ -1,20 +1,13 @@ package com.mycompany.autobackupprogram.Dialogs; -import com.formdev.flatlaf.FlatIntelliJLaf; import com.mycompany.autobackupprogram.Enums.ConfigKey; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationCategory; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationKey; -import com.mycompany.autobackupprogram.Managers.ThemeManager; import com.mycompany.autobackupprogram.Entities.TimeInterval; -import static com.mycompany.autobackupprogram.GUI.BackupManagerGUI.OpenExceptionMessage; - import java.awt.Image; -import java.util.Arrays; import javax.swing.ImageIcon; import javax.swing.JOptionPane; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; public class TimePicker extends javax.swing.JDialog { @@ -28,6 +21,7 @@ public TimePicker(java.awt.Frame parent, TimeInterval timeInterval, boolean moda initComponents(); + this.timeInterval = timeInterval; if (timeInterval != null) { daysSpinner.setValue(timeInterval.getDays()); hoursSpinner.setValue(timeInterval.getHours()); @@ -44,7 +38,7 @@ public TimePicker(java.awt.Frame parent, TimeInterval timeInterval, boolean moda public TimeInterval getTimeInterval() { if (closeOk) return timeInterval; return null; - } + } private void daysIntervalSpinnerChange() { Integer days = (Integer) daysSpinner.getValue(); @@ -188,9 +182,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 294, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) + .addGap(0, 92, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addGroup(layout.createSequentialGroup() @@ -227,11 +221,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(minutesSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel3)) - .addGap(38, 38, 38) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 87, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnOk) .addComponent(jButton2)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) ); pack(); diff --git a/src/main/java/com/mycompany/autobackupprogram/Entities/Backup.java b/src/main/java/com/mycompany/autobackupprogram/Entities/Backup.java index 9d06e08..dd033dd 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Entities/Backup.java +++ b/src/main/java/com/mycompany/autobackupprogram/Entities/Backup.java @@ -2,9 +2,11 @@ import java.time.LocalDateTime; +import com.mycompany.autobackupprogram.JSONConfigReader; +import com.mycompany.autobackupprogram.Enums.ConfigKey; public class Backup { - + private static final JSONConfigReader configReader = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); private String _backupName; private String _initialPath; private String _destinationPath; @@ -16,7 +18,7 @@ public class Backup { private LocalDateTime _creationDate; private LocalDateTime _lastUpdateDate; private int _backupCount; - + private int _maxBackupsToKeep; public Backup() { _backupName = ""; @@ -30,9 +32,10 @@ public Backup() { _creationDate = null; _lastUpdateDate = null; _backupCount = 0; + _maxBackupsToKeep = configReader.getMaxCountForSameBackup(); } - public Backup(String backupName, String initialPath, String destinationPath, LocalDateTime lastBackup, Boolean autoBackup, LocalDateTime nextDateBackup, TimeInterval timeIntervalBackup, String notes, LocalDateTime creationDate, LocalDateTime lastUpdateDate, int backupCount) { + public Backup(String backupName, String initialPath, String destinationPath, LocalDateTime lastBackup, Boolean autoBackup, LocalDateTime nextDateBackup, TimeInterval timeIntervalBackup, String notes, LocalDateTime creationDate, LocalDateTime lastUpdateDate, int backupCount, int maxBackupsToKeep) { this._backupName = backupName; this._initialPath = initialPath; this._destinationPath = destinationPath; @@ -44,6 +47,7 @@ public Backup(String backupName, String initialPath, String destinationPath, Loc this._creationDate = creationDate; this._lastUpdateDate = lastUpdateDate; this._backupCount = backupCount; + this._maxBackupsToKeep = maxBackupsToKeep; } public void UpdateBackup(Backup backupUpdated) { @@ -58,98 +62,85 @@ public void UpdateBackup(Backup backupUpdated) { this._creationDate = backupUpdated.getCreationDate(); this._lastUpdateDate = backupUpdated.getLastUpdateDate(); this._backupCount = backupUpdated.getBackupCount(); + this._maxBackupsToKeep = backupUpdated.getMaxBackupsToKeep(); } @Override public String toString() { - return "[Name: "+_backupName + ", InitialPath: " + _initialPath + ", DestinationPath: " + _destinationPath + ", LastBackup: " + _lastBackup + ", IsAutoBackup: " + _autoBackup + ", NextDate: " + _nextDateBackup + ", Interval: " + (_timeIntervalBackup!=null ? _timeIntervalBackup.toString(): "")+"]"; + return "[Name: "+_backupName + ", InitialPath: " + _initialPath + ", DestinationPath: " + _destinationPath + ", LastBackup: " + _lastBackup + ", IsAutoBackup: " + _autoBackup + ", NextDate: " + _nextDateBackup + ", Interval: " + (_timeIntervalBackup!=null ? _timeIntervalBackup.toString(): "")+", MaxBackupsToKeep: " + _maxBackupsToKeep + "]"; } public String getBackupName() { return _backupName; } - public String getInitialPath() { return _initialPath; } - public String getDestinationPath() { return _destinationPath; } - public LocalDateTime getLastBackup() { return _lastBackup; } - public boolean isAutoBackup() { return _autoBackup; } - public LocalDateTime getNextDateBackup() { return _nextDateBackup; } - public TimeInterval getTimeIntervalBackup() { return _timeIntervalBackup; } - public String getNotes() { return _notes; } - public LocalDateTime getCreationDate() { return _creationDate; } - public LocalDateTime getLastUpdateDate() { return _lastUpdateDate; } - public int getBackupCount() { return _backupCount; } + public int getMaxBackupsToKeep() { + return _maxBackupsToKeep; + } public void setBackupName(String backupName) { this._backupName = backupName; } - public void setInitialPath(String initialPath) { this._initialPath = initialPath; } - public void setDestinationPath(String destinationPath) { this._destinationPath = destinationPath; } - public void setLastBackup(LocalDateTime lastBackup) { this._lastBackup = lastBackup; } - public void setAutoBackup(Boolean autoBackup) { this._autoBackup = autoBackup; } - public void setNextDateBackup(LocalDateTime nextDateBackup) { this._nextDateBackup = nextDateBackup; } - public void setTimeIntervalBackup(TimeInterval timeIntervalBackup) { this._timeIntervalBackup = timeIntervalBackup; } - public void setNotes(String notes) { this._notes = notes; } - public void setCreationDate(LocalDateTime creationDate) { this._creationDate = creationDate; } - public void setLastUpdateDate(LocalDateTime lastUpdateDate) { this._lastUpdateDate = lastUpdateDate; } - public void setBackupCount(int backupCount) { this._backupCount = backupCount; } + public void setMaxBackupsToKeep(int maxBackupsToKeep) { + this._maxBackupsToKeep = maxBackupsToKeep; + } } \ No newline at end of file diff --git a/src/main/java/com/mycompany/autobackupprogram/Entities/BackupList.java b/src/main/java/com/mycompany/autobackupprogram/Entities/BackupList.java new file mode 100644 index 0000000..b02c8f6 --- /dev/null +++ b/src/main/java/com/mycompany/autobackupprogram/Entities/BackupList.java @@ -0,0 +1,24 @@ +package com.mycompany.autobackupprogram.Entities; + +public class BackupList { + private String directory; + private String file; + + public BackupList(String directory, String file) { + this.directory = directory; + this.file = file; + } + + public String getDirectory() { + return directory; + } + public String getFile() { + return file; + } + public void setDirectory(String directory) { + this.directory = directory; + } + public void setFile(String file) { + this.file = file; + } +} diff --git a/src/main/java/com/mycompany/autobackupprogram/Entities/Preferences.java b/src/main/java/com/mycompany/autobackupprogram/Entities/Preferences.java index 30fa9b4..3a53517 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Entities/Preferences.java +++ b/src/main/java/com/mycompany/autobackupprogram/Entities/Preferences.java @@ -1,47 +1,44 @@ package com.mycompany.autobackupprogram.Entities; +import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.Arrays; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import com.mycompany.autobackupprogram.Logger; -import com.mycompany.autobackupprogram.Logger.LogLevel; import com.mycompany.autobackupprogram.Enums.ConfigKey; import com.mycompany.autobackupprogram.Enums.LanguagesEnum; import com.mycompany.autobackupprogram.Enums.ThemesEnum; +import static com.mycompany.autobackupprogram.GUI.BackupManagerGUI.OpenExceptionMessage; +import com.mycompany.autobackupprogram.Logger; +import com.mycompany.autobackupprogram.Logger.LogLevel; public class Preferences { - private static LanguagesEnum language = LanguagesEnum.ENG; - private static ThemesEnum theme = ThemesEnum.INTELLIJ; + private static LanguagesEnum language; + private static ThemesEnum theme; + private static BackupList backupList; public static void loadPreferencesFromJSON() { try (FileReader reader = new FileReader(ConfigKey.CONFIG_DIRECTORY_STRING.getValue() + ConfigKey.PREFERENCES_FILE_STRING.getValue())) { JsonElement jsonElement = JsonParser.parseReader(reader); JsonObject jsonObject = jsonElement.getAsJsonObject(); - - // Map the "Language" JSON value to the LanguagesEnum - String languageFileName = jsonObject.get("Language").getAsString(); - for (LanguagesEnum lang : LanguagesEnum.values()) { - if (lang.getFileName().equals(languageFileName)) { - language = lang; - break; - } - } - // Map the "Theme" JSON value to the ThemesEnum - String themeName = jsonObject.get("Theme").getAsString(); - for (ThemesEnum t : ThemesEnum.values()) { - if (t.getThemeName().equals(themeName)) { - theme = t; - break; - } - } - } catch (Exception e) { - e.printStackTrace(); + language = getLanguageFromJson(jsonObject); + theme = getThemeFromJson(jsonObject); + backupList = getBackupListFromJson(jsonObject); + + updatePreferencesToJSON(); + + } catch (FileNotFoundException e) { + Logger.logMessage("Preferences file not found. Using default preferences.", Logger.LogLevel.WARN); + updatePreferencesToJSON(); // Create the JSON file with default preferences + } catch (Exception ex) { + Logger.logMessage("An error occurred while loading preferences: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } } @@ -52,27 +49,87 @@ public static void updatePreferencesToJSON() { jsonObject.addProperty("Language", language.getFileName()); jsonObject.addProperty("Theme", theme.getThemeName()); + JsonObject backupListObject = new JsonObject(); + backupListObject.addProperty("Directory", backupList.getDirectory()); + backupListObject.addProperty("File", backupList.getFile()); + + jsonObject.add("BackupList", backupListObject); + // Convert JsonObject to JSON string using Gson Gson gson = new Gson(); gson.toJson(jsonObject, writer); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ex) { + Logger.logMessage("An error occurred during updating preferences to json operation: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } } + private static LanguagesEnum getLanguageFromJson(JsonObject jsonObject) { + if (jsonObject.has("Language") && !jsonObject.get("Language").isJsonNull()) { + String languageFileName = jsonObject.get("Language").getAsString(); + for (LanguagesEnum lang : LanguagesEnum.values()) { + if (lang.getFileName().equals(languageFileName)) { + return lang; + } + } + } + return LanguagesEnum.ENG; + } + + private static ThemesEnum getThemeFromJson(JsonObject jsonObject) { + if (jsonObject.has("Theme") && !jsonObject.get("Theme").isJsonNull()) { + String themeName = jsonObject.get("Theme").getAsString(); + for (ThemesEnum t : ThemesEnum.values()) { + if (t.getThemeName().equals(themeName)) { + return t; + } + } + } + return ThemesEnum.INTELLIJ; + } + + private static BackupList getBackupListFromJson(JsonObject jsonObject) { + if (jsonObject.has("BackupList") && !jsonObject.get("BackupList").isJsonNull()) { + JsonObject backupListObject = jsonObject.getAsJsonObject("BackupList"); + + String directory = backupListObject.has("Directory") && !backupListObject.get("Directory").isJsonNull() + ? backupListObject.get("Directory").getAsString() + : ConfigKey.RES_DIRECTORY_STRING.getValue(); + + String file = backupListObject.has("File") && !backupListObject.get("File").isJsonNull() + ? backupListObject.get("File").getAsString() + : ConfigKey.BACKUP_FILE_STRING.getValue() + ConfigKey.VERSION.getValue() + ".json"; + + return new BackupList(directory, file); + } + return getDefaultBackupList(); + } + public static LanguagesEnum getLanguage() { return language; } public static ThemesEnum getTheme() { return theme; } + public static BackupList getBackupList() { + return backupList; + } + public static BackupList getDefaultBackupList() { + return new BackupList( + ConfigKey.RES_DIRECTORY_STRING.getValue(), + ConfigKey.BACKUP_FILE_STRING.getValue() + ConfigKey.VERSION.getValue() + ".json" + ); + } public static void setLanguage(LanguagesEnum language) { Preferences.language = language; } public static void setTheme(ThemesEnum theme) { Preferences.theme = theme; } + public static void setBackupList(BackupList backupList) { + Preferences.backupList = backupList; + } public static void setLanguage(String selectedLanguage) { try { for (LanguagesEnum lang : LanguagesEnum.values()) { @@ -82,9 +139,10 @@ public static void setLanguage(String selectedLanguage) { return; } } - Logger.logMessage("Invalid language name: " + selectedLanguage, LogLevel.ERROR); - } catch (Exception e) { - e.printStackTrace(); + Logger.logMessage("Invalid language name: " + selectedLanguage, LogLevel.WARN); + } catch (Exception ex) { + Logger.logMessage("An error occurred during setting language operation: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } } public static void setTheme(String selectedTheme) { @@ -96,18 +154,10 @@ public static void setTheme(String selectedTheme) { return; } } - Logger.logMessage("Invalid theme name: " + selectedTheme, LogLevel.ERROR); - } catch (Exception e) { - e.printStackTrace(); + Logger.logMessage("Invalid theme name: " + selectedTheme, LogLevel.WARN); + } catch (Exception ex) { + Logger.logMessage("An error occurred during setting theme operation: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } } - - // for test - public static void main(String[] args) { - String CONFIG = "src/main/resources/res/config/config.json"; - ConfigKey.loadFromJson(CONFIG); - loadPreferencesFromJSON(); - updatePreferencesToJSON(); - } - } diff --git a/src/main/java/com/mycompany/autobackupprogram/Enums/ConfigKey.java b/src/main/java/com/mycompany/autobackupprogram/Enums/ConfigKey.java index 3a53b85..cfd3c85 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Enums/ConfigKey.java +++ b/src/main/java/com/mycompany/autobackupprogram/Enums/ConfigKey.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.EnumMap; import java.util.Map; + import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; @@ -22,7 +23,10 @@ public enum ConfigKey { EMAIL, SHARD_WEBSITE, LOGO_IMG, - SHARE_LINK; + SHARE_LINK, + VERSION, + GUI_WIDTH, + GUI_HEIGHT; private static final Map configValues = new EnumMap<>(ConfigKey.class); diff --git a/src/main/java/com/mycompany/autobackupprogram/Enums/LanguagesEnum.java b/src/main/java/com/mycompany/autobackupprogram/Enums/LanguagesEnum.java index 5386edd..926dc8c 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Enums/LanguagesEnum.java +++ b/src/main/java/com/mycompany/autobackupprogram/Enums/LanguagesEnum.java @@ -10,7 +10,7 @@ public enum LanguagesEnum { private final String languageName; private final String fileName; - LanguagesEnum(String languageName, String fileName) { + private LanguagesEnum(String languageName, String fileName) { this.languageName = languageName; this.fileName = fileName; } diff --git a/src/main/java/com/mycompany/autobackupprogram/Enums/MenuItems.java b/src/main/java/com/mycompany/autobackupprogram/Enums/MenuItems.java index 2ea6bc0..7989cb5 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Enums/MenuItems.java +++ b/src/main/java/com/mycompany/autobackupprogram/Enums/MenuItems.java @@ -10,6 +10,8 @@ public enum MenuItems { New, Quit, Save, + Import, + Export, SaveWithName, Share, Support, diff --git a/src/main/java/com/mycompany/autobackupprogram/Enums/ThemesEnum.java b/src/main/java/com/mycompany/autobackupprogram/Enums/ThemesEnum.java index 3a3ba27..a88a741 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Enums/ThemesEnum.java +++ b/src/main/java/com/mycompany/autobackupprogram/Enums/ThemesEnum.java @@ -14,7 +14,7 @@ public enum ThemesEnum { private final String themeName; - ThemesEnum(String themeName) { + private ThemesEnum(String themeName) { this.themeName = themeName; } diff --git a/src/main/java/com/mycompany/autobackupprogram/Enums/TranslationLoaderEnum.java b/src/main/java/com/mycompany/autobackupprogram/Enums/TranslationLoaderEnum.java index 6a0a9e8..7a641f5 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Enums/TranslationLoaderEnum.java +++ b/src/main/java/com/mycompany/autobackupprogram/Enums/TranslationLoaderEnum.java @@ -3,6 +3,10 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; + +import com.mycompany.autobackupprogram.Logger; +import com.mycompany.autobackupprogram.Logger.LogLevel; + import java.io.FileReader; import java.io.IOException; import java.util.HashMap; @@ -68,6 +72,9 @@ public enum TranslationKey { NEW("New", "New"), QUIT("Quit", "Quit"), SAVE("Save", "Save"), + PREFERENCES("Preferences", "Preferences"), + IMPORT("Import", "Import"), + EXPORT("Export", "Export"), SAVE_WITH_NAME("SaveWithName", "Save with name"), SHARE("Share", "Share"), SUPPORT("Support", "Support"), @@ -96,6 +103,8 @@ public enum TranslationKey { SINGLE_BACKUP_TOOLTIP("SingleBackupTooltip", "Perform the backup"), AUTO_BACKUP_TOOLTIP("AutoBackupTooltip", "Enable/Disable automatic backup"), TIME_PICKER_TOOLTIP("TimePickerTooltip", "Time picker"), + MAX_BACKUPS_TO_KEEP("MaxBackupsToKeep", "Max backups to keep"), + MAX_BACKUPS_TO_KEEP_TOOLTIP("MaxBackupsToKeepTooltip", "Maximum number of backups before removing the oldest."), // BackupList BACKUP_NAME_COLUMN("BackupNameColumn", "Backup Name"), @@ -115,6 +124,7 @@ public enum TranslationKey { LAST_UPDATE_DATE_DETAIL("LastUpdateDateDetail", "LastUpdateDate"), BACKUP_COUNT_DETAIL("BackupCountDetail", "BackupCount"), NOTES_DETAIL("NotesDetail", "Notes"), + MAX_BACKUPS_TO_KEEP_DETAIL("MaxBackupsToKeepDetail", "MaxBackupsToKeep"), ADD_BACKUP_TOOLTIP("AddBackupTooltip", "Add new backup"), RESEARCH_BAR_TOOLTIP("ResearchBarTooltip", "Research bar"), RESEARCH_BAR_PLACEHOLDER("ResearchBarPlaceholder", "Search..."), @@ -167,6 +177,10 @@ public enum TranslationKey { BACKUP_NAME_INPUT("BackupNameInput", "Name of the backup"), CONFIRMATION_REQUIRED_TITLE("ConfirmationRequiredTitle", "Confirmation required"), DUPLICATED_BACKUP_NAME_MESSAGE("DuplicatedBackupNameMessage", "A backup with the same name already exists, do you want to overwrite it?"), + BACKUP_LIST_CORRECTLY_EXPORTED_TITLE("BackupListCorrectlyExportedTitle", "Menu Export"), + BACKUP_LIST_CORRECTLY_EXPORTED_MESSAGE("BackupListCorrectlyExportedMessage", "Backup list successfully exported to the Desktop!"), + BACKUP_LIST_CORRECTLY_IMPORTED_TITLE("BackupListCorrectlyImportedTitle", "Menu Import"), + BACKUP_LIST_CORRECTLY_IMPORTED_MESSAGE("BackupListCorrectlyImportedMessage", "Backup list successfully imported!"), BACKUP_NAME_ALREADY_USED_MESSAGE("BackupNameAlreadyUsedMessage", "Backup name already used!"), ERROR_MESSAGE_FOR_INCORRECT_INITIAL_PATH("ErrorMessageForIncorrectInitialPath", "Error during the backup operation: the initial path is incorrect!"), EXCEPTION_MESSAGE_TITLE("ExceptionMessageTitle", "Error..."), @@ -199,6 +213,8 @@ public enum TranslationKey { ERROR_MESSAGE_SAVING_FILE("ErrorMessageForSavingFile", "Error saving file"), ERROR_MESSAGE_PATH_NOT_EXISTING("ErrorMessageForPathNotExisting", "One or both paths do not exist!"), ERROR_MESSAGE_SAME_PATHS_GENERIC("ErrorMessageForSamePaths", "The initial path and destination path cannot be the same. Please choose different paths!"), + ERROR_MESSAGE_FOR_WRONG_FILE_EXTENSION_TITLE("ErrorMessageForWrongFileExtensionTitle", "Invalid File"), + ERROR_MESSAGE_FOR_WRONG_FILE_EXTENSION_MESSAGE("ErrorMessageForWrongFileExtensionMessage", "Error: Please select a valid JSON file."), // InfoPage INFO_PAGE_DESCRIPTION("InfoPageDescription", "Backup automatic system for files with the option to schedule and make backups regularly."), @@ -222,7 +238,7 @@ public enum TranslationKey { } // Constructor to assign both key and default value - TranslationKey(String keyName, String defaultValue) { + private TranslationKey(String keyName, String defaultValue) { this.keyName = keyName; this.defaultValue = defaultValue; } @@ -268,7 +284,7 @@ public static void loadTranslations(String filePath) throws IOException, ParseEx category.addTranslation(translationKey, translationValue); } else { // If the key is not recognized in the enum, log it and use the default value - System.err.println("Warning: Unrecognized key in JSON: " + key + ", using default value."); + Logger.logMessage("Warning: Unrecognized key in JSON: " + key + ", using default value.", LogLevel.WARN); category.addTranslation(translationKey, translationKey.getDefaultValue()); } } diff --git a/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.form b/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.form index 9245087..c8a47a2 100644 --- a/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.form +++ b/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.form @@ -174,6 +174,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -417,15 +443,23 @@ - - - - - - + + + + + + + + + + + + + - + + @@ -465,9 +499,14 @@ - + + + + + + - + @@ -653,6 +692,21 @@ + + + + + + + + + + + + + + + diff --git a/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.java b/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.java index 71e831d..edbba3e 100644 --- a/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.java +++ b/src/main/java/com/mycompany/autobackupprogram/GUI/BackupManagerGUI.java @@ -9,12 +9,14 @@ import com.mycompany.autobackupprogram.Logger; import com.mycompany.autobackupprogram.Dialogs.TimePicker; import com.mycompany.autobackupprogram.Entities.Backup; +import com.mycompany.autobackupprogram.Entities.BackupList; import com.mycompany.autobackupprogram.Entities.Preferences; import com.mycompany.autobackupprogram.Enums.ConfigKey; import com.mycompany.autobackupprogram.Enums.MenuItems; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationCategory; import com.mycompany.autobackupprogram.Enums.TranslationLoaderEnum.TranslationKey; +import com.mycompany.autobackupprogram.Logger.LogLevel; import com.mycompany.autobackupprogram.Managers.ThemeManager; import java.awt.Color; @@ -30,6 +32,10 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -44,6 +50,7 @@ import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; +import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileSystemView; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; @@ -59,28 +66,27 @@ * @author Dennis Turco */ public class BackupManagerGUI extends javax.swing.JFrame { - + private static final JSONConfigReader configReader = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); public static final DateTimeFormatter dateForfolderNameFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH.mm.ss"); public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); public static Backup currentBackup; - private static List backups; private static JSONAutoBackup JSON; public static DefaultTableModel model; private BackupProgressGUI progressBar; private boolean saveChanged; private Integer selectedRow; - private String backupOnText; private String backupOffText; - - private final String current_version = "2.0.3"; + private String current_version; public BackupManagerGUI() { ThemeManager.updateThemeFrame(this); initComponents(); + + current_version = ConfigKey.VERSION.getValue(); // logo application Image icon = new ImageIcon(this.getClass().getResource(ConfigKey.LOGO_IMG.getValue())).getImage(); @@ -94,9 +100,9 @@ public BackupManagerGUI() { File file = new File(System.getProperty("os.name").toLowerCase().contains("win") ? "C:\\Windows\\System32" : "/root"); if (file.canWrite()) { - Logger.logMessage("The application is running with administrator privileges.", Logger.LogLevel.INFO); + Logger.logMessage("The application is running with administrator privileges.", Logger.LogLevel.DEBUG); } else { - Logger.logMessage("The application does NOT have administrator privileges.", Logger.LogLevel.INFO); + Logger.logMessage("The application does NOT have administrator privileges.", Logger.LogLevel.DEBUG); } customListeners(); @@ -116,12 +122,19 @@ public BackupManagerGUI() { MenuShare.setVisible(config.isMenuItemEnabled(MenuItems.Share.name())); MenuSupport.setVisible(config.isMenuItemEnabled(MenuItems.Support.name())); MenuWebsite.setVisible(config.isMenuItemEnabled(MenuItems.Website.name())); + MenuImport.setVisible(config.isMenuItemEnabled(MenuItems.Import.name())); + MenuExport.setVisible(config.isMenuItemEnabled(MenuItems.Export.name())); + // set app size + setScreenSize(); + // icons researchField.putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_ICON, new javax.swing.ImageIcon(getClass().getResource("/res/img/search.png"))); // translations setTranslations(); + + setCurrentBackupMaxBackupsToKeep(configReader.getMaxCountForSameBackup()); } public void showWindow() { @@ -129,6 +142,14 @@ public void showWindow() { toFront(); requestFocus(); } + + private void setScreenSize() { + Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); + int width = Math.min((int) size.getWidth(), Integer.parseInt(ConfigKey.GUI_WIDTH.getValue())); + int height = Math.min((int) size.getHeight(), Integer.parseInt(ConfigKey.GUI_HEIGHT.getValue())); + + this.setSize(width,height); + } private TimeInterval openTimePicker(TimeInterval time) { TimePicker picker = new TimePicker(this, time, true); @@ -137,24 +158,22 @@ private TimeInterval openTimePicker(TimeInterval time) { } private void openPreferences() { - PreferencesDialog prefs = new PreferencesDialog(this, true); - prefs.setVisible(true); + Logger.logMessage("Event --> opening preferences dialog", LogLevel.INFO); - // reload preferences - if (prefs.isApply()) { - Preferences.updatePreferencesToJSON(); - reloadPreferences(); - } - + PreferencesDialog prefs = new PreferencesDialog(this, true, this); + prefs.setVisible(true); } - private void reloadPreferences() { + public void reloadPreferences() { + Logger.logMessage("Reloading preferences", LogLevel.INFO); + // load language try { TranslationLoaderEnum.loadTranslations(ConfigKey.LANGUAGES_DIRECTORY_STRING.getValue() + Preferences.getLanguage().getFileName()); setTranslations(); - } catch (IOException | ParseException e) { - e.printStackTrace(); + } catch (IOException | ParseException ex) { + Logger.logMessage("An error occurred during reloading preferences operation: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } // load theme @@ -203,12 +222,13 @@ private void OpenFolder(String path) { } private void savedChanges(boolean saved) { - if (saved || currentBackup.getBackupName() == null || currentBackup.getBackupName().isEmpty() || (currentBackup.getInitialPath().equals(startPathField.getText())) && currentBackup.getDestinationPath().equals(destinationPathField.getText()) && currentBackup.getNotes().equals(backupNoteTextArea.getText())) { + if (saved) { setCurrentBackupName(currentBackup.getBackupName()); + saveChanged = true; } else { setCurrentBackupName(currentBackup.getBackupName() + "*"); + saveChanged = false; } - saveChanged = saved; } public void setAutoBackupPreference(boolean option) { @@ -360,7 +380,8 @@ private void SaveWithName() { GetNotesTextArea(), dateNow, dateNow, - 0 + 0, + GetMaxBackupsToKeep() ); backups.add(backup); @@ -427,6 +448,8 @@ public void SingleBackup(String path1, String path2) { else break; } + name1 = BackupOperations.removeExtension(name1); + path2 = path2 + "\\" + name1 + " (Backup " + date + ")"; //------------------------------COPY THE FILE OR DIRECTORY------------------------------ @@ -462,6 +485,10 @@ private void setCurrentBackupName(String name) { private void setCurrentBackupNotes(String notes) { backupNoteTextArea.setText(notes); } + + public void setCurrentBackupMaxBackupsToKeep(int maxBackupsCount) { + maxBackupCountSpinner.setValue(maxBackupsCount); + } public void setStringToText() { try { @@ -486,12 +513,14 @@ public void setTextValues() { private void customListeners() { startPathField.getDocument().addDocumentListener(new DocumentListener() { @Override - public void insertUpdate(DocumentEvent e) { - savedChanges(false); + public void insertUpdate(DocumentEvent e) { + somethingHasChanged(); } @Override - public void removeUpdate(DocumentEvent e) {} + public void removeUpdate(DocumentEvent e) { + somethingHasChanged(); + } @Override public void changedUpdate(DocumentEvent e) {} @@ -500,11 +529,13 @@ public void changedUpdate(DocumentEvent e) {} destinationPathField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { - savedChanges(false); + somethingHasChanged(); } @Override - public void removeUpdate(DocumentEvent e) {} + public void removeUpdate(DocumentEvent e) { + somethingHasChanged(); + } @Override public void changedUpdate(DocumentEvent e) {} @@ -513,16 +544,32 @@ public void changedUpdate(DocumentEvent e) {} backupNoteTextArea.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { - savedChanges(false); + somethingHasChanged(); } @Override - public void removeUpdate(DocumentEvent e) {} + public void removeUpdate(DocumentEvent e) { + somethingHasChanged(); + } @Override public void changedUpdate(DocumentEvent e) {} }); } + + private void somethingHasChanged() { + boolean backupNameNotNull = currentBackup.getBackupName() != null; + boolean pathsOrNotesChanged = + !startPathField.getText().equals(currentBackup.getInitialPath()) || + !destinationPathField.getText().equals(currentBackup.getDestinationPath()) || + !backupNoteTextArea.getText().equals(currentBackup.getNotes()); + + if (backupNameNotNull && !pathsOrNotesChanged) { + savedChanges(true); + } else { + savedChanges(false); + } + } public String GetStartPathField() { return startPathField.getText(); @@ -536,6 +583,9 @@ public String GetNotesTextArea() { public boolean GetAutomaticBackupPreference() { return toggleAutoBackup.isSelected(); } + public int GetMaxBackupsToKeep() { + return (int) maxBackupCountSpinner.getValue(); + } public void SetStartPathField(String text) { startPathField.setText(text); } @@ -734,10 +784,10 @@ private static String encodeURI(String value) { } } - public boolean Clear() { + public boolean Clear(boolean canMessageAppear) { Logger.logMessage("Event --> clear", Logger.LogLevel.INFO); - if ((!saveChanged && !currentBackup.getBackupName().isEmpty()) || (!startPathField.getText().isEmpty() || !destinationPathField.getText().isEmpty() || !backupNoteTextArea.getText().isEmpty())) { + if (canMessageAppear && ((!saveChanged && !currentBackup.getBackupName().isEmpty()) || (!startPathField.getText().isEmpty() || !destinationPathField.getText().isEmpty() || !backupNoteTextArea.getText().isEmpty()))) { int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_MESSAGE_FOR_CLEAR), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (response != JOptionPane.YES_OPTION) { return false; @@ -750,6 +800,7 @@ public boolean Clear() { destinationPathField.setText(""); lastBackupLabel.setText(""); backupNoteTextArea.setText(""); + maxBackupCountSpinner.setValue(configReader.getMaxCountForSameBackup()); return true; } @@ -785,6 +836,7 @@ private void saveFile() { currentBackup.setInitialPath(GetStartPathField()); currentBackup.setDestinationPath(GetDestinationPathField()); currentBackup.setNotes(GetNotesTextArea()); + currentBackup.setMaxBackupsToKeep(GetMaxBackupsToKeep()); LocalDateTime dateNow = LocalDateTime.now(); currentBackup.setLastUpdateDate(dateNow); @@ -806,7 +858,8 @@ private void saveFile() { private void OpenBackup(String backupName) { Logger.logMessage("Event --> opening backup", Logger.LogLevel.INFO); - if (!saveChanged) { + // if canges are not saved and if something has been written + if (!saveChanged && (startPathField.getText().length() != 0 || destinationPathField.getText().length() != 0 || backupNoteTextArea.getText().length() != 0)) { int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_MESSAGE_FOR_UNSAVED_CHANGES), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (response == JOptionPane.YES_OPTION) { saveFile(); @@ -844,14 +897,12 @@ private void pathSearchWithFileChooser(JTextField textField, boolean allowFiles) if (returnValue == JFileChooser.APPROVE_OPTION) { File selectedFile = jfc.getSelectedFile(); - // Logga il tipo di elemento selezionato if (selectedFile.isDirectory()) { Logger.logMessage("You selected the directory: " + selectedFile, Logger.LogLevel.INFO); } else if (selectedFile.isFile()) { Logger.logMessage("You selected the file: " + selectedFile, Logger.LogLevel.INFO); } - // Imposta il percorso nel campo di testo textField.setText(selectedFile.toString()); } savedChanges(false); @@ -883,6 +934,7 @@ private void updateCurrentFiedsByBackup(Backup backup) { setAutoBackupPreference(backup.isAutoBackup()); setCurrentBackupName(backup.getBackupName()); setCurrentBackupNotes(backup.getNotes()); + setCurrentBackupMaxBackupsToKeep(backup.getMaxBackupsToKeep()); if (backup.getTimeIntervalBackup() != null) { btnTimePicker.setToolTipText(backup.getTimeIntervalBackup().toString()); @@ -905,12 +957,13 @@ private void NewBackup() { } } - if (!Clear()) { + if (!Clear(false)) { return; } currentBackup = new Backup(); currentBackup.setAutoBackup(false); currentBackup.setBackupName(""); + currentBackup.setMaxBackupsToKeep(configReader.getMaxCountForSameBackup()); // basic auto enable is disabled setAutoBackupPreference(currentBackup.isAutoBackup()); @@ -934,9 +987,36 @@ private void disableAutoBackup(Backup backup) { btnTimePicker.setEnabled(false); } } + + private void maxBackupCountSpinnerChange() { + Integer backupCount = (Integer) maxBackupCountSpinner.getValue(); + + if (backupCount == null || backupCount < 1) { + maxBackupCountSpinner.setValue(1); + } else if (backupCount > 10) { + maxBackupCountSpinner.setValue(10); + } + } + + private void mouseWeel(java.awt.event.MouseWheelEvent evt) { + javax.swing.JSpinner spinner = (javax.swing.JSpinner) evt.getSource(); + int rotation = evt.getWheelRotation(); + + if (rotation < 0) { + spinner.setValue((Integer) spinner.getValue() + 1); + } else { + spinner.setValue((Integer) spinner.getValue() - 1); + } + + if ((int) spinner.getValue() != currentBackup.getMaxBackupsToKeep()) + savedChanges(false); + else + savedChanges(true); + } /** * This method is called from within the constructor to initialize the form. + * * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @@ -977,6 +1057,8 @@ private void initComponents() { btnTimePicker = new javax.swing.JButton(); toggleAutoBackup = new javax.swing.JToggleButton(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0)); + jLabel4 = new javax.swing.JLabel(); + maxBackupCountSpinner = new javax.swing.JSpinner(); jPanel2 = new javax.swing.JPanel(); tablePanel = new javax.swing.JPanel(); addBackupEntryButton = new javax.swing.JButton(); @@ -992,6 +1074,10 @@ private void initComponents() { MenuNew = new javax.swing.JMenuItem(); MenuSave = new javax.swing.JMenuItem(); MenuSaveWithName = new javax.swing.JMenuItem(); + jSeparator4 = new javax.swing.JPopupMenu.Separator(); + MenuImport = new javax.swing.JMenuItem(); + MenuExport = new javax.swing.JMenuItem(); + jSeparator5 = new javax.swing.JPopupMenu.Separator(); MenuClear = new javax.swing.JMenuItem(); MenuHistory = new javax.swing.JMenuItem(); jMenu2 = new javax.swing.JMenu(); @@ -1207,6 +1293,21 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { } }); + jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + jLabel4.setText("Keep only last"); + + maxBackupCountSpinner.setToolTipText("Maximum number of backups before removing the oldest."); + maxBackupCountSpinner.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + maxBackupCountSpinnerStateChanged(evt); + } + }); + maxBackupCountSpinner.addMouseWheelListener(new java.awt.event.MouseWheelListener() { + public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { + maxBackupCountSpinnerMouseWheelMoved(evt); + } + }); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -1236,13 +1337,19 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addComponent(txtTitle, javax.swing.GroupLayout.PREFERRED_SIZE, 466, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(211, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(toggleAutoBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnTimePicker, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(SingleBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(maxBackupCountSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(SingleBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(toggleAutoBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnTimePicker, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(351, 351, 351)) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() @@ -1277,7 +1384,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(toggleAutoBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnTimePicker, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(57, 57, 57)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(maxBackupCountSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel4)) + .addGap(20, 20, 20)) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) @@ -1453,6 +1564,26 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { } }); jMenu1.add(MenuSaveWithName); + jMenu1.add(jSeparator4); + + MenuImport.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/img/import.png"))); // NOI18N + MenuImport.setText("Import backup list"); + MenuImport.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + MenuImportActionPerformed(evt); + } + }); + jMenu1.add(MenuImport); + + MenuExport.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/img/export.png"))); // NOI18N + MenuExport.setText("Export backup list"); + MenuExport.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + MenuExportActionPerformed(evt); + } + }); + jMenu1.add(MenuExport); + jMenu1.add(jSeparator5); MenuClear.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_DOWN_MASK)); MenuClear.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/img/clean.png"))); // NOI18N @@ -1601,7 +1732,7 @@ private void MenuHistoryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-F }//GEN-LAST:event_MenuHistoryActionPerformed private void MenuClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuClearActionPerformed - Clear(); + Clear(true); }//GEN-LAST:event_MenuClearActionPerformed private void MenuSaveWithNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuSaveWithNameActionPerformed @@ -1681,6 +1812,7 @@ else if (SwingUtilities.isLeftMouseButton(evt)) { String lastUpdateDate = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.LAST_UPDATE_DATE_DETAIL); String backupCount = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.BACKUP_COUNT_DETAIL); String notes = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.NOTES_DETAIL); + String maxBackupsToKeep = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.MAX_BACKUPS_TO_KEEP_DETAIL); detailsLabel.setText( "" + backupName + ": " + backups.get(selectedRow).getBackupName() + ", " + @@ -1692,6 +1824,7 @@ else if (SwingUtilities.isLeftMouseButton(evt)) { "" + creationDate + ": " + (backups.get(selectedRow).getCreationDate() != null ? backups.get(selectedRow).getCreationDate().format(formatter) : "_") + ", " + "" + lastUpdateDate + ": " + (backups.get(selectedRow).getLastUpdateDate() != null ? backups.get(selectedRow).getLastUpdateDate().format(formatter) : "_") + ", " + "" + backupCount + ": " + (backups.get(selectedRow).getBackupCount()) + ", " + + "" + maxBackupsToKeep + ": " + (backups.get(selectedRow).getMaxBackupsToKeep()) + ", " + "" + notes + ": " + (backups.get(selectedRow).getNotes()) + "" ); @@ -1721,7 +1854,8 @@ private void DuplicatePopupItemActionPerformed(java.awt.event.ActionEvent evt) { backup.getNotes(), dateNow, dateNow, - 0 + 0, + backup.getMaxBackupsToKeep() ); backups.add(newBackup); @@ -1819,9 +1953,7 @@ private void MenuShareActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIR private void toggleAutoBackupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_toggleAutoBackupActionPerformed Logger.logMessage("Event --> Changing auto backup preference", Logger.LogLevel.INFO); - - System.out.println(currentBackup.toString()); - + // checks if (!BackupOperations.CheckInputCorrect(currentBackup.getBackupName(),startPathField.getText(), destinationPathField.getText(), null)) { toggleAutoBackup.setSelected(false); @@ -1850,8 +1982,6 @@ private void toggleAutoBackupActionPerformed(java.awt.event.ActionEvent evt) {// toggleAutoBackup.setText(toggleAutoBackup.isSelected() ? backupOnText : backupOffText); currentBackup.setAutoBackup(enabled); BackupOperations.updateBackupList(backups); - - System.out.println(currentBackup.toString()); }//GEN-LAST:event_toggleAutoBackupActionPerformed private void MenuWebsiteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuWebsiteActionPerformed @@ -1866,7 +1996,7 @@ private void MenuSupportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-F Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.MAIL)) { - String subject = "Support - Auto Backup Program"; + String subject = "Support - Backup Manager"; String mailTo = "mailto:" + ConfigKey.EMAIL.getValue() + "?subject=" + encodeURI(subject); try { @@ -1901,7 +2031,6 @@ private void btnPathSearch1ActionPerformed(java.awt.event.ActionEvent evt) {//GE private void btnTimePickerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTimePickerActionPerformed TimeInterval timeInterval = openTimePicker(currentBackup.getTimeIntervalBackup()); - if (timeInterval == null) return; btnTimePicker.setToolTipText(timeInterval.toString()); @@ -1911,7 +2040,6 @@ private void btnTimePickerActionPerformed(java.awt.event.ActionEvent evt) {//GEN currentBackup.setTimeIntervalBackup(timeInterval); currentBackup.setNextDateBackup(nextDateBackup); - currentBackup.setInitialPath(GetStartPathField()); currentBackup.setDestinationPath(GetDestinationPathField()); for (Backup b : backups) { @@ -1930,9 +2058,70 @@ private void MenuPreferencesActionPerformed(java.awt.event.ActionEvent evt) {//G openPreferences(); }//GEN-LAST:event_MenuPreferencesActionPerformed + private void MenuImportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuImportActionPerformed + Logger.logMessage("Event --> importing backup list", Logger.LogLevel.INFO); + + JFileChooser jfc = new JFileChooser(ConfigKey.RES_DIRECTORY_STRING.getValue()); + jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); + + FileNameExtensionFilter jsonFilter = new FileNameExtensionFilter("JSON Files (*.json)", "json"); + jfc.setFileFilter(jsonFilter); + int returnValue = jfc.showSaveDialog(null); + + if (returnValue == JFileChooser.APPROVE_OPTION) { + File selectedFile = jfc.getSelectedFile(); + if (selectedFile.isFile() && selectedFile.getName().toLowerCase().endsWith(".json")) { + Logger.logMessage("File imported: " + selectedFile, Logger.LogLevel.INFO); + + Preferences.setBackupList(new BackupList(selectedFile.getParent()+File.separator, selectedFile.getName())); + Preferences.updatePreferencesToJSON(); + + try { + backups = JSON.ReadBackupListFromJSON(Preferences.getBackupList().getDirectory(), Preferences.getBackupList().getFile()); + BackupOperations.updateTableWithNewBackupList(backups); + } catch (IOException ex) { + Logger.logMessage("An error occurred: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + } + + JOptionPane.showMessageDialog(this, TranslationCategory.DIALOGS.getTranslation(TranslationKey.BACKUP_LIST_CORRECTLY_IMPORTED_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.BACKUP_LIST_CORRECTLY_IMPORTED_TITLE), JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(this, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_WRONG_FILE_EXTENSION_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_WRONG_FILE_EXTENSION_TITLE), JOptionPane.ERROR_MESSAGE); + } + } + }//GEN-LAST:event_MenuImportActionPerformed + + private void MenuExportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuExportActionPerformed + Logger.logMessage("Event --> exporting backup list", Logger.LogLevel.INFO); + + Path desktopPath = Paths.get(System.getProperty("user.home"), "Desktop", Preferences.getBackupList().getFile()); + Path sourcePath = Paths.get(Preferences.getBackupList().getDirectory() + Preferences.getBackupList().getFile()); + + try { + Files.copy(sourcePath, desktopPath, StandardCopyOption.REPLACE_EXISTING); + JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.BACKUP_LIST_CORRECTLY_EXPORTED_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.BACKUP_LIST_CORRECTLY_EXPORTED_TITLE), JOptionPane.INFORMATION_MESSAGE); + } catch (java.nio.file.NoSuchFileException ex) { + Logger.logMessage("Source file not found: " + ex.getMessage(), Logger.LogLevel.ERROR); + JOptionPane.showMessageDialog(null, "Error: The source file was not found.\nPlease check the file path.", "Export Error", JOptionPane.ERROR_MESSAGE); + } catch (java.nio.file.AccessDeniedException ex) { + Logger.logMessage("Access denied to desktop: " + ex.getMessage(), Logger.LogLevel.ERROR); + JOptionPane.showMessageDialog(null, "Error: Access to the Desktop is denied.\nPlease check folder permissions and try again.","Export Error", JOptionPane.ERROR_MESSAGE); + } catch (IOException ex) { + Logger.logMessage("Unexpected error: " + ex.getMessage(), Logger.LogLevel.ERROR); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); + } + }//GEN-LAST:event_MenuExportActionPerformed + + private void maxBackupCountSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maxBackupCountSpinnerStateChanged + maxBackupCountSpinnerChange(); + }//GEN-LAST:event_maxBackupCountSpinnerStateChanged + + private void maxBackupCountSpinnerMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_maxBackupCountSpinnerMouseWheelMoved + mouseWeel(evt); + }//GEN-LAST:event_maxBackupCountSpinnerMouseWheelMoved + private void setTranslations() { try { - backups = JSON.ReadBackupListFromJSON(ConfigKey.BACKUP_FILE_STRING.getValue(), ConfigKey.RES_DIRECTORY_STRING.getValue()); + backups = JSON.ReadBackupListFromJSON(Preferences.getBackupList().getDirectory(), Preferences.getBackupList().getFile()); displayBackupList(backups); } catch (IOException ex) { backups = null; @@ -1962,6 +2151,9 @@ private void setTranslations() { MenuQuit.setText(TranslationCategory.MENU.getTranslation(TranslationKey.QUIT)); MenuSave.setText(TranslationCategory.MENU.getTranslation(TranslationKey.SAVE)); MenuSaveWithName.setText(TranslationCategory.MENU.getTranslation(TranslationKey.SAVE_WITH_NAME)); + MenuPreferences.setText(TranslationCategory.MENU.getTranslation(TranslationKey.PREFERENCES)); + MenuImport.setText(TranslationCategory.MENU.getTranslation(TranslationKey.IMPORT)); + MenuExport.setText(TranslationCategory.MENU.getTranslation(TranslationKey.EXPORT)); MenuShare.setText(TranslationCategory.MENU.getTranslation(TranslationKey.SHARE)); MenuSupport.setText(TranslationCategory.MENU.getTranslation(TranslationKey.SUPPORT)); MenuWebsite.setText(TranslationCategory.MENU.getTranslation(TranslationKey.WEBSITE)); @@ -1985,6 +2177,8 @@ private void setTranslations() { startPathField.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.INITIAL_PATH_PLACEHOLDER)); destinationPathField.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.DESTINATION_PATH_PLACEHOLDER)); btnTimePicker.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.TIME_PICKER_TOOLTIP)); + maxBackupCountSpinner.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.MAX_BACKUPS_TO_KEEP_TOOLTIP).toString() + "\n" + TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.SPINNER_TOOLTIP).toString()); + jLabel4.setText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.MAX_BACKUPS_TO_KEEP)); // backup list addBackupEntryButton.setToolTipText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.ADD_BACKUP_TOOLTIP)); @@ -2019,7 +2213,9 @@ private void setTranslations() { private javax.swing.JMenuItem MenuBugReport; private javax.swing.JMenuItem MenuClear; private javax.swing.JMenuItem MenuDonate; + private javax.swing.JMenuItem MenuExport; private javax.swing.JMenuItem MenuHistory; + private javax.swing.JMenuItem MenuImport; private javax.swing.JMenuItem MenuInfoPage; private javax.swing.JMenuItem MenuNew; private javax.swing.JMenuItem MenuPreferences; @@ -2048,6 +2244,7 @@ private void setTranslations() { private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; private javax.swing.JMenu jMenu1; private javax.swing.JMenu jMenu2; private javax.swing.JMenu jMenu3; @@ -2061,7 +2258,10 @@ private void setTranslations() { private javax.swing.JPopupMenu.Separator jSeparator1; private javax.swing.JPopupMenu.Separator jSeparator2; private javax.swing.JPopupMenu.Separator jSeparator3; + private javax.swing.JPopupMenu.Separator jSeparator4; + private javax.swing.JPopupMenu.Separator jSeparator5; private javax.swing.JLabel lastBackupLabel; + private javax.swing.JSpinner maxBackupCountSpinner; private javax.swing.JMenuItem renamePopupItem; private javax.swing.JTextField researchField; private javax.swing.JTextField startPathField; diff --git a/src/main/java/com/mycompany/autobackupprogram/Interfaces/IJSONAutoBackup.java b/src/main/java/com/mycompany/autobackupprogram/Interfaces/IJSONAutoBackup.java index cd081f8..95779d8 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Interfaces/IJSONAutoBackup.java +++ b/src/main/java/com/mycompany/autobackupprogram/Interfaces/IJSONAutoBackup.java @@ -6,7 +6,7 @@ import com.mycompany.autobackupprogram.Entities.Backup; public interface IJSONAutoBackup { - public List ReadBackupListFromJSON(String filename, String directoryPath) throws IOException; - public void UpdateBackupListJSON(String filename, String directoryPath, List backups); - public void UpdateSingleBackupInJSON(String filename, String directoryPath, Backup updatedBackup); + public List ReadBackupListFromJSON(String directoryPath, String filename) throws IOException; + public void UpdateBackupListJSON(String directoryPath, String filename, List backups); + public void UpdateSingleBackupInJSON(String directoryPath, String filename, Backup updatedBackup); } diff --git a/src/main/java/com/mycompany/autobackupprogram/JSONAutoBackup.java b/src/main/java/com/mycompany/autobackupprogram/JSONAutoBackup.java index 5214495..ff78dbb 100644 --- a/src/main/java/com/mycompany/autobackupprogram/JSONAutoBackup.java +++ b/src/main/java/com/mycompany/autobackupprogram/JSONAutoBackup.java @@ -1,35 +1,58 @@ package com.mycompany.autobackupprogram; -import static com.mycompany.autobackupprogram.GUI.BackupManagerGUI.OpenExceptionMessage; - -import java.io.*; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import com.mycompany.autobackupprogram.Entities.Backup; +import com.mycompany.autobackupprogram.Entities.Preferences; import com.mycompany.autobackupprogram.Entities.TimeInterval; +import static com.mycompany.autobackupprogram.GUI.BackupManagerGUI.OpenExceptionMessage; import com.mycompany.autobackupprogram.Interfaces.IJSONAutoBackup; +import com.mycompany.autobackupprogram.Logger.LogLevel; public class JSONAutoBackup implements IJSONAutoBackup { @Override - public List ReadBackupListFromJSON(String filename, String directoryPath) throws IOException { - + public List ReadBackupListFromJSON(String directoryPath, String filename) throws IOException { List backupList = new ArrayList<>(); + + // check if the directory is correct, otherwise we have to reset to default + File directory = new File(directoryPath); + if (!directory.exists() || !directory.isDirectory()) { + Logger.logMessage("Directory of the backup list file doesn't exists (" + directoryPath + "), resetted to default value.", LogLevel.INFO); + Preferences.setBackupList(Preferences.getDefaultBackupList()); + Preferences.updatePreferencesToJSON(); + directoryPath = Preferences.getBackupList().getDirectory(); + } + String filePath = directoryPath + filename; + File file = new File(filePath); // Check if the file exists and is not empty - File file = new File(filePath); - if (!file.exists() || file.length() == 0) { - Logger.logMessage("The file does not exist or is empty: " + filePath, Logger.LogLevel.WARN); - return backupList; + if (!file.exists()) { + file.createNewFile(); + Logger.logMessage("New backup list created with name: " + filePath, LogLevel.INFO); } - + if (file.length() == 0) { + try (FileWriter writer = new FileWriter(file)) { + writer.write("[]"); + Logger.logMessage("File initialized with empty JSON array: []", LogLevel.INFO); + } catch (IOException e) { + Logger.logMessage("Error initializing file: " + e.getMessage(), LogLevel.ERROR, e); + throw e; + } + } + JSONParser parser = new JSONParser(); try (FileReader reader = new FileReader(filePath)) { @@ -46,6 +69,7 @@ public List ReadBackupListFromJSON(String filename, String directoryPath String creationDateStr = (String) backupObj.get("creation_date"); String lastUpdateDateStr = (String) backupObj.get("last_update_date"); int backupCountValue = Math.toIntExact((Long) backupObj.get("backup_count")); + int maxBackupsToKeepValue = Math.toIntExact((Long) backupObj.get("max_backups_to_keep")); Object value = backupObj.get("automatic_backup"); Boolean automaticBackupValue = null; @@ -75,20 +99,20 @@ public List ReadBackupListFromJSON(String filename, String directoryPath notesValue, creationDateValue, lastUpdateDateValue, - backupCountValue + backupCountValue, + maxBackupsToKeepValue )); } - } catch (IOException | ParseException ex) { + } catch (IOException | ParseException | NullPointerException ex) { Logger.logMessage("An error occurred: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - ex.printStackTrace(); } return backupList; } @Override - public void UpdateBackupListJSON(String filename, String directoryPath, List backups) { + public void UpdateBackupListJSON(String directoryPath, String filename, List backups) { String filePath = directoryPath + filename; JSONArray updatedBackupArray = new JSONArray(); @@ -105,6 +129,7 @@ public void UpdateBackupListJSON(String filename, String directoryPath, List 0 && args[0].equalsIgnoreCase("--background"); diff --git a/src/main/java/com/mycompany/autobackupprogram/Managers/ThemeManager.java b/src/main/java/com/mycompany/autobackupprogram/Managers/ThemeManager.java index 7030b24..4ddb7ee 100644 --- a/src/main/java/com/mycompany/autobackupprogram/Managers/ThemeManager.java +++ b/src/main/java/com/mycompany/autobackupprogram/Managers/ThemeManager.java @@ -1,12 +1,15 @@ package com.mycompany.autobackupprogram.Managers; +import java.awt.Component; import java.awt.Dialog; import java.awt.Frame; +import java.util.Arrays; import javax.swing.SwingUtilities; import javax.swing.JPopupMenu; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; +import static com.mycompany.autobackupprogram.GUI.BackupManagerGUI.OpenExceptionMessage; import com.formdev.flatlaf.FlatDarculaLaf; import com.formdev.flatlaf.FlatIntelliJLaf; @@ -19,6 +22,7 @@ import com.formdev.flatlaf.intellijthemes.FlatSolarizedDarkIJTheme; import com.formdev.flatlaf.intellijthemes.FlatSolarizedLightIJTheme; import com.mycompany.autobackupprogram.Entities.Preferences; +import com.mycompany.autobackupprogram.Logger; // https://www.formdev.com/flatlaf/#demo // https://www.formdev.com/flatlaf/themes/ @@ -28,27 +32,27 @@ public class ThemeManager { public static void updateThemeFrame(Frame frame) { updateTheme(); - - // Update all components and Revalidate and repaint - SwingUtilities.updateComponentTreeUI(frame); - frame.revalidate(); - frame.repaint(); + repaint(frame); } public static void refreshPopup(JPopupMenu popup) { - // Update all components and Revalidate and repaint - SwingUtilities.updateComponentTreeUI(popup); - popup.revalidate(); - popup.repaint(); + repaint(popup); } public static void updateThemeDialog(Dialog dialog) { updateTheme(); - - // Update all components and Revalidate and repaint - SwingUtilities.updateComponentTreeUI(dialog); - dialog.revalidate(); - dialog.repaint(); + repaint(dialog); + } + + private static void repaint(Object objectToRepaint) { + if (objectToRepaint instanceof Dialog || objectToRepaint instanceof JPopupMenu || objectToRepaint instanceof Frame) { + // Update all components and revalidate and repaint + SwingUtilities.updateComponentTreeUI((Component) objectToRepaint); + ((Component) objectToRepaint).revalidate(); + ((Component) objectToRepaint).repaint(); + } else { + throw new IllegalArgumentException("Unsupported object type for repainting: " + objectToRepaint.getClass().getName()); + } } private static void updateTheme() { @@ -93,7 +97,8 @@ private static void updateTheme() { } } catch (UnsupportedLookAndFeelException ex) { - System.err.println("Error setting LookAndFeel: " + ex.getMessage()); + Logger.logMessage("Error setting LookAndFeel: " + ex.getMessage(), Logger.LogLevel.ERROR, ex); + OpenExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); } } } \ No newline at end of file diff --git a/src/main/resources/res/backup_list.json b/src/main/resources/res/backup_list.json index 90d87a1..e69de29 100644 --- a/src/main/resources/res/backup_list.json +++ b/src/main/resources/res/backup_list.json @@ -1 +0,0 @@ -[{"time_interval_backup":"2.0:0","destination_path":"C:\\Users\\Utente\\Desktop","automatic_backup":true,"backup_name":"prova","notes":"","backup_count":4,"next_date_backup":"2024-11-19T20:49:21.617100500","start_path":"C:\\Users\\Utente\\Desktop\\AutoBackupProgram","creation_date":"2024-11-14T00:38:57.913908","last_backup":"2024-11-17T20:49:21.617100500","last_update_date":"2024-11-14T09:14:50.262976200"},{"time_interval_backup":null,"destination_path":"C:\\Users\\Utente\\Desktop","automatic_backup":false,"backup_name":"asd","notes":"","backup_count":3,"next_date_backup":null,"start_path":"C:\\Users\\Utente\\Desktop\\gg","creation_date":"2024-11-16T14:27:45.841538600","last_backup":"2024-11-17T22:08:01.249689600","last_update_date":"2024-11-17T22:12:25.156933700"}] \ No newline at end of file diff --git a/src/main/resources/res/backup_list2.0.4.json b/src/main/resources/res/backup_list2.0.4.json new file mode 100644 index 0000000..760bc8f --- /dev/null +++ b/src/main/resources/res/backup_list2.0.4.json @@ -0,0 +1 @@ +[{"time_interval_backup":null,"destination_path":"C:\\Users\\Utente\\Desktop","automatic_backup":false,"backup_name":"testone","notes":"","backup_count":28,"next_date_backup":null,"max_backups_to_keep":3,"start_path":"C:\\Users\\Utente\\Desktop\\DJI_20240925130642_0030_D.JPG","creation_date":"2024-12-12T21:44:37.283133900","last_backup":"2024-12-15T18:24:01.604149400","last_update_date":"2024-12-15T19:18:30.219573400"},{"time_interval_backup":null,"destination_path":"C:\\Users\\Utente\\Desktop","automatic_backup":false,"backup_name":"test","notes":"","backup_count":9,"next_date_backup":null,"max_backups_to_keep":3,"start_path":"C:\\Users\\Utente\\Desktop\\gg","creation_date":"2024-12-12T22:16:07.418800400","last_backup":"2024-12-15T18:23:46.142024700","last_update_date":"2024-12-15T19:48:22.694237500"},{"time_interval_backup":null,"destination_path":"C:\\Users\\Utente\\Desktop","automatic_backup":false,"backup_name":"test2","notes":"","backup_count":9,"next_date_backup":null,"max_backups_to_keep":3,"start_path":"C:\\Users\\Utente\\Desktop\\test.txt","creation_date":"2024-12-12T22:52:41.381624500","last_backup":"2024-12-12T23:29:01.963425500","last_update_date":"2024-12-15T19:42:11.680337300"},{"time_interval_backup":null,"destination_path":"C:\\Users\\Utente\\Desktop","automatic_backup":false,"backup_name":"testoso","notes":"","backup_count":1,"next_date_backup":null,"max_backups_to_keep":3,"start_path":"C:\\Users\\Utente\\Desktop\\test testoso","creation_date":"2024-12-14T15:25:02.587785800","last_backup":"2024-12-14T15:55:17.694068","last_update_date":"2024-12-15T18:56:37.110488"}] \ No newline at end of file diff --git a/src/main/resources/res/config/config.json b/src/main/resources/res/config/config.json index c506fcb..9bf3caa 100644 --- a/src/main/resources/res/config/config.json +++ b/src/main/resources/res/config/config.json @@ -1,6 +1,6 @@ { "LOG_FILE_STRING": "log_file", - "BACKUP_FILE_STRING": "backup_list.json", + "BACKUP_FILE_STRING": "backup_list", "CONFIG_FILE_STRING": "config.json", "PREFERENCES_FILE_STRING": "preferences.json", "RES_DIRECTORY_STRING": "src/main/resources/res/", @@ -13,6 +13,9 @@ "EMAIL": "assistenza@shardpc.it", "SHARD_WEBSITE": "https://www.shardpc.it/", "LOGO_IMG": "/res/img/logo.png", + "VERSION": "2.0.4", + "GUI_WIDTH": "982", + "GUI_HEIGHT": "715", "MenuItems": { "BugReport": true, @@ -24,6 +27,8 @@ "New": true, "Quit": true, "Save": true, + "Import": true, + "Export": true, "SaveWithName": true, "Share": true, "Support": true, @@ -35,7 +40,6 @@ "type": "int", "description": "Interval in minutes to check for auto backup" }, - "LogService": { "ERROR": true, "INFO": true, @@ -50,6 +54,11 @@ "value": 1000, "type": "int", "description": "Lines to keep after file clear" + }, + "MaxCountForSameBackup": { + "value": 3, + "type": "int", + "description": "Max count for the same backup in the same directory" } } } \ No newline at end of file diff --git a/src/main/resources/res/config/preferences.json b/src/main/resources/res/config/preferences.json index 7ef571d..ddb4942 100644 --- a/src/main/resources/res/config/preferences.json +++ b/src/main/resources/res/config/preferences.json @@ -1 +1 @@ -{"Language":"eng.json","Theme":"Light"} \ No newline at end of file +{"Language":"deu.json","Theme":"Carbon","BackupList":{"Directory":"src/main/resources/res/","File":"backup_list2.0.4.json"}} \ No newline at end of file diff --git a/src/main/resources/res/img/export.png b/src/main/resources/res/img/export.png new file mode 100644 index 0000000..115b125 Binary files /dev/null and b/src/main/resources/res/img/export.png differ diff --git a/src/main/resources/res/img/import.png b/src/main/resources/res/img/import.png new file mode 100644 index 0000000..45624d4 Binary files /dev/null and b/src/main/resources/res/img/import.png differ diff --git a/src/main/resources/res/languages/deu.json b/src/main/resources/res/languages/deu.json index f130187..3598b59 100644 --- a/src/main/resources/res/languages/deu.json +++ b/src/main/resources/res/languages/deu.json @@ -24,6 +24,9 @@ "Quit": "Beenden", "Save": "Speichern", "SaveWithName": "Speichern unter", + "Preferences": "Einstellungen", + "Import": "Backup-Liste importieren", + "Export": "Backup-Liste exportieren", "Share": "Teilen", "Support": "Support", "Website": "Webseite" @@ -50,7 +53,9 @@ "NotesTooltip": "(Optional) Backup-Beschreibung", "SingleBackupTooltip": "Backup durchführen", "AutoBackupTooltip": "Automatisches Backup aktivieren/deaktivieren", - "TimePickerTooltip": "Zeitwähler" + "TimePickerTooltip": "Zeitwähler", + "MaxBackupsToKeep": "Maximale Anzahl an Sicherungen beibehalten", + "MaxBackupsToKeepTooltip": "Maximale Anzahl an Sicherungen, bevor die ältesten entfernt werden." }, "BackupList": { "BackupNameColumn": "Backup-Name", @@ -63,13 +68,14 @@ "BackupNameDetail": "Backup-Name", "InitialPathDetail": "Anfangspfad", "DestinationPathDetail": "Zielpfad", - "LastBackupDetail": "Letztes Backup", - "NextBackupDateDetail": "Nächstes Backup", + "LastBackupDetail": "LetztesBackup", + "NextBackupDateDetail": "NächstesBackup", "TimeIntervalDetail": "Zeitintervall", "CreationDateDetail": "Erstellungsdatum", - "LastUpdateDateDetail": "Letzte Aktualisierung", + "LastUpdateDateDetail": "LetzteAktualisierung", "BackupCountDetail": "Backup-Anzahl", "NotesDetail": "Notizen", + "MaxBackupsToKeepDetail": "MaximaleSicherungenBehalten", "AddBackupTooltip": "Neues Backup hinzufügen", "ResearchBarTooltip": "Suchleiste", "ResearchBarPlaceholder": "Suchen...", @@ -147,7 +153,13 @@ "ErrorMessageInputMissingGeneric": "Eingabe fehlt!", "ErrorMessageForSavingFile": "Fehler beim Speichern der Datei", "ErrorMessageForPathNotExisting": "Ein oder beide Pfade existieren nicht!", - "ErrorMessageForSamePaths": "Der Anfangspfad und der Zielpfad dürfen nicht gleich sein. Bitte wählen Sie unterschiedliche Pfade!" + "ErrorMessageForSamePaths": "Der Anfangspfad und der Zielpfad dürfen nicht gleich sein. Bitte wählen Sie unterschiedliche Pfade!", + "BackupListCorrectlyExportedTitle": "Menü Exportieren", + "BackupListCorrectlyExportedMessage": "Backup-Liste erfolgreich auf den Desktop exportiert!", + "BackupListCorrectlyImportedTitle": "Menü Importieren", + "BackupListCorrectlyImportedMessage": "Backup-Liste erfolgreich importiert!", + "ErrorMessageForWrongFileExtensionTitle": "Ungültige Datei", + "ErrorMessageForWrongFileExtensionMessage": "Fehler: Bitte wählen Sie eine gültige JSON-Datei aus." } } \ No newline at end of file diff --git a/src/main/resources/res/languages/eng.json b/src/main/resources/res/languages/eng.json index 8568121..7a9173e 100644 --- a/src/main/resources/res/languages/eng.json +++ b/src/main/resources/res/languages/eng.json @@ -24,6 +24,9 @@ "Quit": "Quit", "Save": "Save", "SaveWithName": "Save with name", + "Preferences": "Preferences", + "Import": "Import backup list", + "Export": "Export backup list", "Share": "Share", "Support": "Support", "Website": "Website" @@ -50,7 +53,9 @@ "NotesTooltip": "(Optional) Backup description", "SingleBackupTooltip": "Perform the backup", "AutoBackupTooltip": "Enable/Disable automatic backup", - "TimePickerTooltip": "Time picker" + "TimePickerTooltip": "Time picker", + "MaxBackupsToKeep": "Max backups to keep", + "MaxBackupsToKeepTooltip": "Maximum number of backups before removing the oldest." }, "BackupList": { "BackupNameColumn": "Backup Name", @@ -70,6 +75,7 @@ "LastUpdateDateDetail": "LastUpdateDate", "BackupCountDetail": "BackupCount", "NotesDetail": "Notes", + "MaxBackupsToKeepDetail": "MaxBackupsToKeep", "AddBackupTooltip": "Add new backup", "ResearchBarTooltip": "Research bar", "ResearchBarPlaceholder": "Search...", @@ -147,6 +153,12 @@ "ErrorMessageInputMissingGeneric": "Input Missing!", "ErrorMessageForSavingFile": "Error saving file", "ErrorMessageForPathNotExisting": "One or both paths do not exist!", - "ErrorMessageForSamePaths": "The initial path and destination path cannot be the same. Please choose different paths!" + "BackupListCorrectlyExportedTitle": "Menu Export", + "BackupListCorrectlyExportedMessage": "Backup list successfully exported to the Desktop!", + "BackupListCorrectlyImportedTitle": "Menu Import", + "BackupListCorrectlyImportedMessage": "Backup list successfully imported!", + "ErrorMessageForSamePaths": "The initial path and destination path cannot be the same. Please choose different paths!", + "ErrorMessageForWrongFileExtensionTitle": "Invalid File", + "ErrorMessageForWrongFileExtensionMessage": "Error: Please select a valid JSON file." } } \ No newline at end of file diff --git a/src/main/resources/res/languages/esp.json b/src/main/resources/res/languages/esp.json index bec5a0b..e08e114 100644 --- a/src/main/resources/res/languages/esp.json +++ b/src/main/resources/res/languages/esp.json @@ -24,6 +24,9 @@ "Quit": "Salir", "Save": "Guardar", "SaveWithName": "Guardar con nombre", + "Preferences": "Preferencias", + "Import": "Importar lista de copias de seguridad", + "Export": "Exportar lista de copias de seguridad", "Share": "Compartir", "Support": "Soporte", "Website": "Sitio web" @@ -50,7 +53,9 @@ "NotesTooltip": "(Opcional) Descripción de la copia de seguridad", "SingleBackupTooltip": "Realizar la copia de seguridad", "AutoBackupTooltip": "Activar/Desactivar copia de seguridad automática", - "TimePickerTooltip": "Selector de tiempo" + "TimePickerTooltip": "Selector de tiempo", + "MaxBackupsToKeep": "Máximo de copias de seguridad a mantener", + "MaxBackupsToKeepTooltip": "Número máximo de copias de seguridad antes de eliminar las más antiguas." }, "BackupList": { "BackupNameColumn": "Nombre de la Copia de Seguridad", @@ -60,16 +65,17 @@ "AutomaticBackupColumn": "Copia Automática", "NextBackupDateColumn": "Próxima Fecha de Copia", "TimeIntervalColumn": "Intervalo de Tiempo", - "BackupNameDetail": "Nombre de la Copia", - "InitialPathDetail": "Ruta Inicial", - "DestinationPathDetail": "Ruta de Destino", - "LastBackupDetail": "Última Copia", - "NextBackupDateDetail": "Próxima Fecha", - "TimeIntervalDetail": "Intervalo de Tiempo", - "CreationDateDetail": "Fecha de Creación", - "LastUpdateDateDetail": "Última Actualización", - "BackupCountDetail": "Número de Copias", + "BackupNameDetail": "NombreCopia", + "InitialPathDetail": "RutaInicial", + "DestinationPathDetail": "RutaDestino", + "LastBackupDetail": "ÚltimaCopia", + "NextBackupDateDetail": "PróximaFecha", + "TimeIntervalDetail": "IntervaloTiempo", + "CreationDateDetail": "FechaCreación", + "LastUpdateDateDetail": "ÚltimaActualización", + "BackupCountDetail": "NúmeroCopias", "NotesDetail": "Notas", + "MaxBackupsToKeepDetail": "MaximoCopiasDeSeguridadMantener", "AddBackupTooltip": "Agregar nueva copia de seguridad", "ResearchBarTooltip": "Barra de búsqueda", "ResearchBarPlaceholder": "Buscar...", @@ -147,7 +153,13 @@ "ErrorMessageInputMissingGeneric": "¡Faltan datos de entrada!", "ErrorMessageForSavingFile": "Error al guardar el archivo", "ErrorMessageForPathNotExisting": "¡Una o ambas rutas no existen!", - "ErrorMessageForSamePaths": "La ruta inicial y la de destino no pueden ser iguales. ¡Elija rutas diferentes!" + "ErrorMessageForSamePaths": "La ruta inicial y la de destino no pueden ser iguales. ¡Elija rutas diferentes!", + "BackupListCorrectlyExportedTitle": "Menú Exportar", + "BackupListCorrectlyExportedMessage": "¡Lista de copias de seguridad exportada correctamente al escritorio!", + "BackupListCorrectlyImportedTitle": "Menú Importar", + "BackupListCorrectlyImportedMessage": "¡Lista de copias de seguridad importada correctamente!", + "ErrorMessageForWrongFileExtensionTitle": "Archivo no válido", + "ErrorMessageForWrongFileExtensionMessage": "Error: por favor selecciona un archivo JSON válido." } } \ No newline at end of file diff --git a/src/main/resources/res/languages/fra.json b/src/main/resources/res/languages/fra.json index 8d7b449..f9e6dbe 100644 --- a/src/main/resources/res/languages/fra.json +++ b/src/main/resources/res/languages/fra.json @@ -24,6 +24,9 @@ "Quit": "Quitter", "Save": "Enregistrer", "SaveWithName": "Enregistrer sous", + "Preferences": "Préférences", + "Import": "Importer la liste de sauvegarde", + "Export": "Exporter la liste de sauvegarde", "Share": "Partager", "Support": "Assistance", "Website": "Site web" @@ -50,7 +53,9 @@ "NotesTooltip": "(Optionnel) Description de la sauvegarde", "SingleBackupTooltip": "Effectuer la sauvegarde", "AutoBackupTooltip": "Activer/Désactiver la sauvegarde automatique", - "TimePickerTooltip": "Sélecteur de temps" + "TimePickerTooltip": "Sélecteur de temps", + "MaxBackupsToKeep": "Nombre maximum de sauvegardes à conserver", + "MaxBackupsToKeepTooltip": "Nombre maximum de sauvegardes avant de supprimer les plus anciennes." }, "BackupList": { "BackupNameColumn": "Nom de la Sauvegarde", @@ -60,16 +65,17 @@ "AutomaticBackupColumn": "Sauvegarde Automatique", "NextBackupDateColumn": "Prochaine Date de Sauvegarde", "TimeIntervalColumn": "Intervalle de Temps", - "BackupNameDetail": "Nom de la Sauvegarde", - "InitialPathDetail": "Chemin Initial", - "DestinationPathDetail": "Chemin de Destination", - "LastBackupDetail": "Dernière Sauvegarde", - "NextBackupDateDetail": "Prochaine Sauvegarde", - "TimeIntervalDetail": "Intervalle de Temps", - "CreationDateDetail": "Date de Création", - "LastUpdateDateDetail": "Dernière Mise à Jour", - "BackupCountDetail": "Nombre de Sauvegardes", + "BackupNameDetail": "NomSauvegarde", + "InitialPathDetail": "CheminInitial", + "DestinationPathDetail": "CheminDestination", + "LastBackupDetail": "DernièreSauvegarde", + "NextBackupDateDetail": "ProchaineSauvegarde", + "TimeIntervalDetail": "IntervalleTemps", + "CreationDateDetail": "DateCréation", + "LastUpdateDateDetail": "DernièreMiseJour", + "BackupCountDetail": "NombreSauvegardes", "NotesDetail": "Notes", + "MaxBackupsToKeepDetail": "NombreMaxSauvegardesConserver", "AddBackupTooltip": "Ajouter une nouvelle sauvegarde", "ResearchBarTooltip": "Barre de recherche", "ResearchBarPlaceholder": "Rechercher...", @@ -147,7 +153,13 @@ "ErrorMessageInputMissingGeneric": "Entrée manquante !", "ErrorMessageForSavingFile": "Erreur lors de l'enregistrement du fichier", "ErrorMessageForPathNotExisting": "Un ou les deux chemins n'existent pas !", - "ErrorMessageForSamePaths": "Le chemin initial et le chemin de destination ne peuvent pas être identiques. Veuillez choisir des chemins différents !" + "ErrorMessageForSamePaths": "Le chemin initial et le chemin de destination ne peuvent pas être identiques. Veuillez choisir des chemins différents !", + "BackupListCorrectlyExportedTitle": "Menu Exporter", + "BackupListCorrectlyExportedMessage": "Liste de sauvegarde exportée avec succès sur le bureau !", + "BackupListCorrectlyImportedTitle": "Menu Importer", + "BackupListCorrectlyImportedMessage": "Liste de sauvegarde importée avec succès !", + "ErrorMessageForWrongFileExtensionTitle": "Fichier invalide", + "ErrorMessageForWrongFileExtensionMessage": "Erreur : Veuillez sélectionner un fichier JSON valide." } } \ No newline at end of file diff --git a/src/main/resources/res/languages/ita.json b/src/main/resources/res/languages/ita.json index e2b2bf0..1c4ad4f 100644 --- a/src/main/resources/res/languages/ita.json +++ b/src/main/resources/res/languages/ita.json @@ -24,6 +24,9 @@ "Quit": "Esci", "Save": "Salva", "SaveWithName": "Salva con nome", + "Preferences": "Preferenze", + "Import": "Importa lista di backup", + "Export": "Esporta lista di backup", "Share": "Condividi", "Support": "Supporto", "Website": "Sito web" @@ -50,7 +53,9 @@ "NotesTooltip": "(Opzionale) Descrizione del backup", "SingleBackupTooltip": "Esegui il backup", "AutoBackupTooltip": "Attiva/Disattiva backup automatico", - "TimePickerTooltip": "Selettore orario" + "TimePickerTooltip": "Selettore orario", + "MaxBackupsToKeep": "Massimo di backup da mantenere", + "MaxBackupsToKeepTooltip": "Numero massimo di backup da conservare prima di eliminare i più vecchi." }, "BackupList": { "BackupNameColumn": "Nome del Backup", @@ -69,6 +74,7 @@ "CreationDateDetail": "DataCreazione", "LastUpdateDateDetail": "DataUltimoAggiornamento", "BackupCountDetail": "ConteggioBackup", + "MaxBackupsToKeepDetail": "MassimoBackupDaMantenere", "NotesDetail": "Note", "AddBackupTooltip": "Aggiungi nuovo backup", "ResearchBarTooltip": "Barra di ricerca", @@ -147,6 +153,12 @@ "ErrorMessageInputMissingGeneric": "Input Mancanti!", "ErrorMessageForSavingFile": "Errore nel salvataggio del file", "ErrorMessageForPathNotExisting": "Uno o entrambi i percorsi non esistono!", - "ErrorMessageForSamePaths": "Il percorso iniziale e il percorso di destinazione non possono essere uguali. Si prega di scegliere percorsi diversi!" + "ErrorMessageForSamePaths": "Il percorso iniziale e il percorso di destinazione non possono essere uguali. Si prega di scegliere percorsi diversi!", + "BackupListCorrectlyExportedTitle": "Menu Esporta", + "BackupListCorrectlyExportedMessage": "Lista di backup esportata correttamente sul desktop!", + "BackupListCorrectlyImportedTitle": "Menu Importa", + "BackupListCorrectlyImportedMessage": "Lista di backup importata correttamente!", + "ErrorMessageForWrongFileExtensionTitle": "File non valido", + "ErrorMessageForWrongFileExtensionMessage": "Errore: seleziona un file JSON valido." } } \ No newline at end of file diff --git a/src/test/java/test/TestBackupManagerGUI.java b/src/test/java/test/TestBackupManagerGUI.java deleted file mode 100644 index b0f4aa6..0000000 --- a/src/test/java/test/TestBackupManagerGUI.java +++ /dev/null @@ -1,22 +0,0 @@ -package test; - -import org.junit.jupiter.api.BeforeEach; - -import com.mycompany.autobackupprogram.GUI.BackupManagerGUI; - -import org.junit.jupiter.api.AfterEach; - -public class TestBackupManagerGUI { - - private BackupManagerGUI program; - - @BeforeEach - void setup() { - program = new BackupManagerGUI(); - } - - @AfterEach - void tearDown() { - program = null; - } -} \ No newline at end of file diff --git a/src/test/java/test/TestConfigKey.java b/src/test/java/test/TestConfigKey.java index 419ab71..d89fd06 100644 --- a/src/test/java/test/TestConfigKey.java +++ b/src/test/java/test/TestConfigKey.java @@ -11,7 +11,6 @@ import java.io.IOException; public class TestConfigKey { - private final String LOG_FILE_STRING = "log_file"; private final String BACKUP_FILE_STRING = "backup_list.json"; private final String CONFIG_FILE_STRING = "config.json"; diff --git a/src/test/java/test/TestJSONAutoBackup.java b/src/test/java/test/TestJSONAutoBackup.java deleted file mode 100644 index 992f1d2..0000000 --- a/src/test/java/test/TestJSONAutoBackup.java +++ /dev/null @@ -1,22 +0,0 @@ -package test; - -import com.mycompany.autobackupprogram.JSONAutoBackup; -import com.mycompany.autobackupprogram.Interfaces.IJSONAutoBackup; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.AfterEach; - -public class TestJSONAutoBackup { - - private IJSONAutoBackup json; - - @BeforeEach - void setup() { - json = new JSONAutoBackup(); - } - - @AfterEach - void tearDown() { - json = null; - } -} \ No newline at end of file diff --git a/src/test/java/test/TestPreferences.java b/src/test/java/test/TestPreferences.java new file mode 100644 index 0000000..72ad495 --- /dev/null +++ b/src/test/java/test/TestPreferences.java @@ -0,0 +1,50 @@ +package test; + +import com.mycompany.autobackupprogram.Entities.Preferences; +import com.mycompany.autobackupprogram.Enums.ConfigKey; +import com.mycompany.autobackupprogram.Enums.LanguagesEnum; +import com.mycompany.autobackupprogram.Enums.ThemesEnum; +import com.mycompany.autobackupprogram.Logger; + +import java.io.File; +import java.io.IOException; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; + +public class TestPreferences { + + private static File temp_log_file; + private static final String CONFIG = "src/main/resources/res/config/config.json"; + + @BeforeAll + static void setUpBeforeClass() throws IOException { + ConfigKey.loadFromJson(CONFIG); + + temp_log_file = File.createTempFile("src/test/resources/temp_log_file", ""); + Logger.setLogFilePath(temp_log_file.getPath()); + } + + @Test + void testUpdatePreferences() { + Preferences.setLanguage(LanguagesEnum.DEU); + Preferences.setTheme(ThemesEnum.CARBON); + + Preferences.updatePreferencesToJSON(); // update + Preferences.loadPreferencesFromJSON(); // reload + + // check if update changed everything correctly + assertEquals(LanguagesEnum.DEU.getLanguageName(), Preferences.getLanguage().getLanguageName()); + assertEquals(ThemesEnum.CARBON.getThemeName(), Preferences.getTheme().getThemeName()); + } + + @AfterEach + void tearDown() { + if (temp_log_file != null && temp_log_file.exists()) { + temp_log_file.delete(); + } + } +} \ No newline at end of file