diff --git a/.vscode/launch.json b/.vscode/launch.json index 7d83c636..a8ade45c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,66 +6,18 @@ "configurations": [ { "type": "java", - "name": "EncryptConfigFile", - "request": "launch", - "mainClass": "backupmanager.Email.EncryptConfigFile", - "projectName": "BackupManager" - }, - { - "type": "java", - "name": "ConfigReader", - "request": "launch", - "mainClass": "backupmanager.Email.ConfigReader", - "projectName": "BackupManager" - }, - { - "type": "java", - "name": "SidebarTest", - "request": "launch", - "mainClass": "test.SidebarTest", - "projectName": "BackupManager" - }, - { - "type": "java", - "name": "DecryptPassword", - "request": "launch", - "mainClass": "backupmanager.Email.DecryptPassword", - "projectName": "BackupManager" - }, - { - "type": "java", - "name": "EncryptPassword", - "request": "launch", - "mainClass": "backupmanager.Email.EncryptPassword", - "projectName": "BackupManager" - }, - { - "type": "java", - "name": "Current File", - "request": "launch", - "mainClass": "${file}" - }, - { - "type": "java", - "name": "TranslationLoaderEnum", - "request": "launch", - "mainClass": "backupmanager.Enums.TranslationLoaderEnum", - "projectName": "BackupManager" - }, - { - "type": "java", - "name": "MainApp", + "name": "Backup Manager", "request": "launch", "mainClass": "backupmanager.MainApp", - "projectName": "BackupManager" + "projectName": "backupmanager" }, { "type": "java", "name": "Background service", "request": "launch", "mainClass": "backupmanager.MainApp", - "projectName": "BackupManager", + "projectName": "backupmanager", "args": "--background" } ] -} \ No newline at end of file +} diff --git a/BackupManager_convert_to_exe_launch4j.xml b/BackupManager_convert_to_exe_launch4j.xml index c1068722..5e66f66f 100644 --- a/BackupManager_convert_to_exe_launch4j.xml +++ b/BackupManager_convert_to_exe_launch4j.xml @@ -2,7 +2,7 @@ false gui - C:\Users\Dennis\Documents\Programmazione\BackupManager\target\BackupManager-1.0-SNAPSHOT-jar-with-dependencies.jar + C:\Users\Dennis\Documents\Programmazione\BackupManager\target\backupmanager-2.6.1-jar-with-dependencies.jar C:\Users\Dennis\Documents\Programmazione\BackupManager\BackupManager.exe @@ -25,11 +25,11 @@ - 2.2.1.0 + 3.0.0.0 2.0.RC1 Backup management and automation utility Copyright © 2024 Shard - 2.2.1.0 + 3.0.0.0 2.0.RC1 Backup Manager Shard diff --git a/BackupManager_installer_inno_setup.iss b/BackupManager_installer_inno_setup.iss index 3c2e8b6f..1c033de4 100644 --- a/BackupManager_installer_inno_setup.iss +++ b/BackupManager_installer_inno_setup.iss @@ -4,14 +4,14 @@ [Setup] AppName=BackupManager -AppVersion=2.2.1 +AppVersion=3.0.0 AppPublisher=Shard AppPublisherURL=https://www.shardpc.it/ DefaultDirName={userdocs}\Shard\BackupManager DisableDirPage=yes DisableProgramGroupPage=no PrivilegesRequired=lowest -OutputBaseFilename=BackupManager_v2.2.1_Setup +OutputBaseFilename=BackupManager_v3.0.0_Setup SetupIconFile=src\main\resources\res\img\logo.ico SetupLogging=yes Compression=lzma @@ -37,7 +37,6 @@ Source: "config.enc"; DestDir: "{app}" Source: "jre\*"; DestDir: "{app}\jre"; Flags: recursesubdirs Source: "src\main\resources\*"; DestDir: "{app}\src\main\resources"; Flags: recursesubdirs -Source: "docs\*"; DestDir: "{app}\docs"; Flags: recursesubdirs ; ========================================= ; AVVIO AUTOMATICO (PER-UTENTE) diff --git a/code_documentation.md b/code_documentation.md index 752615ad..f7267df5 100644 --- a/code_documentation.md +++ b/code_documentation.md @@ -157,3 +157,7 @@ Backup Manager is intentionally designed to be: * Minimal in external dependencies The goal is to avoid enterprise-level complexity while maintaining production-grade stability. + +## Build + +To build the project: `mvn clean install` diff --git a/pom.xml b/pom.xml index dc2ec52c..4f292359 100644 --- a/pom.xml +++ b/pom.xml @@ -1,13 +1,68 @@ + 4.0.0 - backupmanager - BackupManager - 1.0-SNAPSHOT + io.github.dj-raven + backupmanager + 2.6.1 jar + 21 + 21 UTF-8 + + + + Central Portal Snapshots + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + + + + io.github.dj-raven + modal-dialog + 2.6.1 + + + + io.github.dj-raven + swing-datetime-picker + 2.1.4-SNAPSHOT + + + io.github.dj-raven + swing-color-picker + 2.0.0 + + + io.github.dj-raven + swing-pack + 1.0.1-SNAPSHOT + + + org.jfree + jfreechart + 1.5.6 + + + + com.formdev + flatlaf-intellij-themes + 3.7 + + + com.formdev + flatlaf-fonts-roboto + 2.137 + + + com.fifesoft + rsyntaxtextarea + 3.4.0 + + com.google.code.gson @@ -47,42 +102,12 @@ test - - - com.formdev - flatlaf-intellij-themes - 3.5.2 - - - com.formdev - flatlaf - 3.4.1 - - - com.formdev - flatlaf-extras - 3.5.4 - org.netbeans.external AbsoluteLayout RELEASE220 - - - com.itextpdf - kernel - 7.2.5 - jar - - - com.itextpdf - layout - 7.2.5 - jar - - com.kenai.nbpwr @@ -120,12 +145,6 @@ sqlite-jdbc 3.45.2.0 - - - org.jfree - jfreechart - 1.5.4 - @@ -161,7 +180,7 @@ - + diff --git a/src/main/java/backupmanager/BackupOperations.java b/src/main/java/backupmanager/BackupOperations.java index 0d146c70..8de33e2c 100644 --- a/src/main/java/backupmanager/BackupOperations.java +++ b/src/main/java/backupmanager/BackupOperations.java @@ -23,35 +23,48 @@ import backupmanager.Entities.ZippingContext; import backupmanager.Enums.BackupTriggerType; import backupmanager.Enums.ErrorType; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.BackupManagerGUI; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; import backupmanager.Helpers.BackupHelper; import static backupmanager.Helpers.BackupHelper.dateForfolderNameFormatter; -import static backupmanager.Helpers.BackupHelper.formatter; import backupmanager.Managers.ExceptionManager; import backupmanager.Services.RunningBackupService; import backupmanager.Services.ZippingThread; -import backupmanager.Table.TableDataManager; import backupmanager.Utils.FolderUtils; +import backupmanager.Utils.ModalUtils; import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.gui.menu.DrawerManager; +import raven.modal.component.SimpleModalBorder; public class BackupOperations { private static final Logger logger = LoggerFactory.getLogger(BackupOperations.class); - public static void singleBackup(ZippingContext context, BackupTriggerType triggeredBy) { - if (context.backup() == null) throw new IllegalArgumentException("Backup cannot be null!"); + + public static void requestSingleBackup(ZippingContext context, BackupTriggerType triggeredBy) { + switch (triggeredBy) { + case USER -> { + if (!BackupRequestRepository.isAnyBackupRunning()) + singleBackup(context, triggeredBy); + else + ModalUtils.showWarning(DrawerManager.getInstance().getParent(), Translations.get(TKey.WARNING_GENERIC_TITLE), Translations.get(TKey.WARNING_BACKUP_ALREADY_IN_PROGRESS_MESSAGE), SimpleModalBorder.CLOSE_OPTION); + } + case SCHEDULER -> singleBackup(context, triggeredBy); + } + } + + private static void singleBackup(ZippingContext context, BackupTriggerType triggeredBy) { + if (context.execution().backup() == null) throw new IllegalArgumentException("Backup cannot be null!"); logger.info("Event --> manual backup started"); try { - String path1 = context.backup().getTargetPath(); - String path2 = context.backup().getDestinationPath(); + String path1 = context.execution().backup().getTargetPath(); + String path2 = context.execution().backup().getDestinationPath(); - if(!checkInputCorrect(context.backup().getName(), path1, path2, context.trayIcon())) + if(!checkInputCorrect(context.execution().backup().getName(), path1, path2, context.ui().trayIcon())) return; - if (context.progressBar() != null) - context.progressBar().setVisible(true); + if (context.ui().progressBar() != null) + context.ui().progressBar().setVisible(true); LocalDateTime dateNow = LocalDateTime.now(); String date = dateNow.format(dateForfolderNameFormatter); @@ -82,7 +95,7 @@ public static void executeBackup(ZippingContext context, BackupTriggerType trigg private static void createBackupRequest(ZippingContext context, BackupTriggerType triggeredBy, File sourceFile, File outputFile, int totalFilesCount) { long targetSize = FolderUtils.calculateFileOrFolderSize(sourceFile.getAbsolutePath()); - BackupRequestRepository.insertBackupRequest(BackupRequest.createNewBackupRequest(context.backup().getId(), triggeredBy, outputFile.getAbsolutePath(), targetSize, totalFilesCount)); + BackupRequestRepository.insertBackupRequest(BackupRequest.createNewBackupRequest(context.execution().backup().getId(), triggeredBy, outputFile.getAbsolutePath(), targetSize, totalFilesCount)); } public static String removeExtension(String fileName) { @@ -93,7 +106,7 @@ public static String removeExtension(String fileName) { } private static void updateAfterBackup(String path1, String path2, ZippingContext context) { - if (context.backup() == null) throw new IllegalArgumentException("Backup cannot be null!"); + if (context.execution().backup() == null) throw new IllegalArgumentException("Backup cannot be null!"); if (path1 == null) throw new IllegalArgumentException("Initial path cannot be null!"); if (path2 == null) throw new IllegalArgumentException("Destination path cannot be null!"); @@ -102,31 +115,31 @@ private static void updateAfterBackup(String path1, String path2, ZippingContext reEnableButtonsAndTable(context); // next day backup update - if (context.backup().isAutomatic() == true) { - TimeInterval time = context.backup().getTimeIntervalBackup(); + if (context.execution().backup().isAutomatic()) { + TimeInterval time = context.execution().backup().getTimeIntervalBackup(); LocalDateTime nextDateBackup = BackupHelper.getNexDateBackup(time); - context.backup().setNextBackupDate(nextDateBackup); + context.execution().backup().setNextBackupDate(nextDateBackup); logger.info("Next date backup setted to: " + nextDateBackup); } - context.backup().setLastBackupDate(LocalDateTime.now()); - context.backup().setCount(context.backup().getCount()+1); + context.execution().backup().setLastBackupDate(LocalDateTime.now()); + context.execution().backup().setCount(context.execution().backup().getCount()+1); try { List backups = BackupHelper.getBackupList(); for (ConfigurationBackup b : backups) { - if (b.getName().equals(context.backup().getName())) { - b.updateBackup(context.backup()); + if (b.getName().equals(context.execution().backup().getName())) { + b.updateBackup(context.execution().backup()); break; } } - BackupHelper.updateBackup(context.backup()); + BackupHelper.updateBackup(context.execution().backup()); - logger.info("Backup :\"" + context.backup().getName() + "\" updated after the backup"); + logger.info("Backup :\"" + context.execution().backup().getName() + "\" updated after the backup"); - if (context.trayIcon() != null) - context.trayIcon().displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + context.backup().getName() + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.SUCCESS_MESSAGE) + "\n" + TranslationCategory.GENERAL.getTranslation(TranslationKey.FROM) + ": " + path1 + "\n" + TranslationCategory.GENERAL.getTranslation(TranslationKey.TO) + ": " + path2, TrayIcon.MessageType.INFO); + if (context.ui().trayIcon() != null) + context.ui().trayIcon().displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + context.execution().backup().getName() + Translations.get(TKey.SUCCESS_MESSAGE) + "\n" + Translations.get(TKey.FROM) + ": " + path1 + "\n" + Translations.get(TKey.TO) + ": " + path2, TrayIcon.MessageType.INFO); } catch (IllegalArgumentException ex) { logger.error("An error occurred: " + ex.getMessage(), ex); ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); @@ -134,7 +147,7 @@ private static void updateAfterBackup(String path1, String path2, ZippingContext } public static String pathSearchWithFileChooser(boolean allowFiles) { - logger.info("Event --> File chooser"); + logger.debug("File chooser, " + " files allowed: " + false); JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory()); @@ -185,31 +198,31 @@ public static void interruptBackupProcess(ZippingContext context) { if (ZippingThread.isInterrupted()) reEnableButtonsAndTable(context); - if (context.progressBar() != null) - context.progressBar().dispose(); + if (context.ui().progressBar() != null) + context.ui().progressBar().dispose(); } public static void reEnableButtonsAndTable(ZippingContext context) { - if (context.interruptBackupPopupItem() != null) context.interruptBackupPopupItem().setEnabled(false); - if (context.deleteBackupPopupItem() != null) context.deleteBackupPopupItem().setEnabled(true); + if (context.ui().interruptBackupPopupItem() != null) context.ui().interruptBackupPopupItem().setEnabled(false); + if (context.ui().deleteBackupPopupItem() != null) context.ui().deleteBackupPopupItem().setEnabled(true); - RunningBackupService.updateBackupStatusAfterCompletitionByBackupConfigurationId(context.backup().getId()); + RunningBackupService.updateBackupStatusAfterCompletitionByBackupConfigurationId(context.execution().backup().getId()); - if (BackupManagerGUI.backupTable != null) - TableDataManager.removeProgressInTheTableAndRestoreAsDefault(context.backup(), formatter); + if (context.ui().backupTableService() != null) + context.ui().backupTableService().removeProgress(context.execution().backup()); } public static void updateProgressPercentage(int value, String path1, String path2, ZippingContext context, String fileProcessed, int filesCopiedSoFar, int totalFilesCount) { if (value == 0 || value == 25 || value == 50 || value == 75 || value == 100) logger.info("Zipping progress: " + value + "%"); - if (context.progressBar() != null) - context.progressBar().updateProgressBar(value, fileProcessed, filesCopiedSoFar, totalFilesCount); + if (context.ui().progressBar() != null) + context.ui().progressBar().updateProgressBar(value, fileProcessed, filesCopiedSoFar, totalFilesCount); - if (BackupManagerGUI.backupTable != null) - TableDataManager.updateProgressBarPercentage(context.backup(), value, formatter); + if (context.ui().backupTableService() != null) + context.ui().backupTableService().updateProgress(context.execution().backup(), value); - BackupRequest request = BackupRequestRepository.getLastBackupInProgressByConfigurationId(context.backup().getId()); + BackupRequest request = BackupRequestRepository.getLastBackupInProgressByConfigurationId(context.execution().backup().getId()); if (request != null) { if (value < 100) @@ -218,7 +231,7 @@ else if (value == 100) { RunningBackupService.updateBackupZippedFolderSizeById(request.backupRequestId(), path2); updateAfterBackup(path1, path2, context); - deleteOldBackupsIfNecessary(context.backup().getMaxToKeep(), path2); + deleteOldBackupsIfNecessary(context.execution().backup().getMaxToKeep(), path2); } } } @@ -280,101 +293,62 @@ public static void deletePotentiallyIncompletedBackupsFromLastExecution() { List requests = BackupRequestRepository.getRunningBackups(); if (requests != null) { for (BackupRequest request : requests) { - boolean deleted = deletePartialBackup(request.outputPath()); - if (deleted) { - BackupHelper.forceBackupTermination(request.backupRequestId()); - } + ZippingThread.stopExecutorService(1); + BackupHelper.forceBackupTermination(request); } } } - private static boolean deletePartialBackup(String filePath) { - logger.info("Attempting to delete partial backup: " + filePath); - - ZippingThread.stopExecutorService(1); - - if (filePath == null || filePath.isEmpty()) { - logger.warn("The file path is null or empty."); - return false; - } - - File file = new File(filePath); - - // Check if the file exists and is a valid file - if (file.exists()) { - if (file.isFile()) { - try { - if (file.delete()) { - logger.info("Partial backup deleted successfully: " + file.getName()); - return true; - } else { - logger.warn("Failed to delete partial backup (delete failed): " + file.getName()); - } - } catch (SecurityException e) { - logger.error("Security exception occurred while attempting to delete: " + file.getName(), e); - } catch (Exception e) { - logger.error("Unexpected error while attempting to delete: " + file.getName(), e); - } - } else { - logger.warn("The path points to a directory, not a file: " + filePath); - } - } else { - logger.warn("The file does not exist: " + filePath); - } - - return false; -} - public static void setError(ErrorType error, TrayIcon trayIcon, String backupName) { switch (error) { case InputMissing -> { logger.warn("Input Missing!"); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_INPUT_MISSING), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_INPUT_MISSING), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_INPUT_MISSING_GENERIC), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_INPUT_MISSING_GENERIC), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } case InputError -> { logger.warn("Input Error! One or both paths do not exist."); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_FILES_NOT_EXISTING), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_FILES_NOT_EXISTING), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_PATH_NOT_EXISTING), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_PATH_NOT_EXISTING), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } case SamePaths -> { logger.warn("The initial path and destination path cannot be the same. Please choose different paths"); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_SAME_PATHS), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_SAME_PATHS), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_SAME_PATHS_GENERIC), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_SAME_PATHS_GENERIC), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } case ErrorCountingFiles -> { logger.warn("Error during counting files in directory"); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_COUNTING_FILES), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_COUNTING_FILES), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_COUNTING_FILES), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_COUNTING_FILES), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } case ZippingGenericError -> { logger.warn("Error during zipping directory"); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_ZIPPING_GENERIC), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_ZIPPING_GENERIC), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_ZIPPING_GENERIC), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_ZIPPING_GENERIC), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } case ZippingIOError -> { logger.warn("I/O error occurred while zipping directory"); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_ZIPPING_IO), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_ZIPPING_IO), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_ZIPPING_IO), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_ZIPPING_IO), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } case ZippingSecurityError -> { logger.warn("Security exception while zipping directory"); if (trayIcon != null) - trayIcon.displayMessage(TranslationCategory.GENERAL.getTranslation(TranslationKey.APP_NAME), TranslationCategory.GENERAL.getTranslation(TranslationKey.BACKUP) + ": " + backupName + TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.ERROR_MESSAGE_ZIPPING_SECURITY), TrayIcon.MessageType.ERROR); + trayIcon.displayMessage(Translations.get(TKey.APP_NAME), Translations.get(TKey.BACKUP) + ": " + backupName + Translations.get(TKey.ERROR_MESSAGE_ZIPPING_SECURITY), TrayIcon.MessageType.ERROR); else - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_ZIPPING_SECURITY), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Translations.get(TKey.ERROR_MESSAGE_ZIPPING_SECURITY), Translations.get(TKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); } default -> throw new IllegalArgumentException("Error type not recognized: " + error); } diff --git a/src/main/java/backupmanager/Charts.java b/src/main/java/backupmanager/Charts.java deleted file mode 100644 index 78470ba8..00000000 --- a/src/main/java/backupmanager/Charts.java +++ /dev/null @@ -1,28 +0,0 @@ -package backupmanager; - -import javax.swing.JPanel; - -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.data.category.DefaultCategoryDataset; - -public class Charts { - public static void createChart(JPanel panelChart) { - DefaultCategoryDataset dataset = new DefaultCategoryDataset(); - dataset.addValue(5, "Clicks", "2025-07-10"); - dataset.addValue(8, "Clicks", "2025-07-11"); - dataset.addValue(3, "Clicks", "2025-07-12"); - - JFreeChart chart = ChartFactory.createBarChart( - "Clicks per Giorno", "Data", "Clicks", dataset); - - ChartPanel chartPanel = new ChartPanel(chart); - chartPanel.setPreferredSize(new java.awt.Dimension(700, 500)); - - panelChart.setLayout(new java.awt.BorderLayout()); - panelChart.removeAll(); - panelChart.add(chartPanel, java.awt.BorderLayout.CENTER); - panelChart.validate(); - } -} diff --git a/src/main/java/backupmanager/Controllers/AppController.java b/src/main/java/backupmanager/Controllers/AppController.java deleted file mode 100644 index 5766bf73..00000000 --- a/src/main/java/backupmanager/Controllers/AppController.java +++ /dev/null @@ -1,101 +0,0 @@ -package backupmanager.Controllers; - -import java.awt.Frame; -import java.io.IOException; -import java.time.LocalDate; - -import javax.swing.JFrame; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.BackupOperations; -import backupmanager.Entities.Confingurations; -import backupmanager.Entities.Subscription; -import backupmanager.Enums.ConfigKey; -import backupmanager.GUI.BackupManagerGUI; -import backupmanager.Helpers.SubscriptionNotifier; -import backupmanager.Json.JSONConfigReader; -import backupmanager.Services.BackgroundService; -import backupmanager.database.Repositories.SubscriptionRepository; - -public class AppController { - private static final JSONConfigReader configReader = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); - private static final Logger logger = LoggerFactory.getLogger(AppController.class); - - private BackupManagerGUI guiInstance; - - private final BackgroundService backgroundService; - private final TrayController trayController; - - public static AppController startBackgroundProcess() throws IOException { - return new AppController(); - } - - private AppController() throws IOException { - logger.info("Starting BackupManager application"); - - this.backgroundService = new BackgroundService(); - - this.trayController = new TrayController( - this::openGui, - this::exitApp - ); - - BackupOperations.deletePotentiallyIncompletedBackupsFromLastExecution(); - - trayController.start(); - - if (canBackgroundServiceStartsBasedOnSubscription()) { - logger.info("Backup service starting in the background"); - backgroundService.start(this.trayController); - } - } - - private boolean canBackgroundServiceStartsBasedOnSubscription() { - if (!Confingurations.isSubscriptionNedded()) return true; - - Subscription subscription = SubscriptionRepository.getAnySubscriptionValid(); - - if (subscription == null) { - logger.info("Subscription expired alert"); - SubscriptionNotifier.showExpiredAlert(trayController); - return false; - } - - int days = configReader.getConfigValue("SubscriptionWarningDays", 7); - LocalDate now = LocalDate.now(); - LocalDate endMinusDays = subscription.endDate().minusDays(days); - - if (now.isAfter(endMinusDays)) { - logger.info("Subscription is expiring alert"); - SubscriptionNotifier.showExpiringWarning(trayController); - } - - return true; - } - - private void openGui() { - logger.info("Opening main GUI"); - - if (guiInstance == null) { - guiInstance = new BackupManagerGUI(); - guiInstance.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); - } - - guiInstance.setVisible(true); - guiInstance.toFront(); - guiInstance.requestFocus(); - - if (guiInstance.getState() == Frame.ICONIFIED) { - guiInstance.setState(Frame.NORMAL); - } - } - - private void exitApp() { - logger.info("Exiting application"); - - backgroundService.stop(); - System.exit(0); - } -} diff --git a/src/main/java/backupmanager/Controllers/EntryUserController.java b/src/main/java/backupmanager/Controllers/EntryUserController.java deleted file mode 100644 index 5e0ed2c5..00000000 --- a/src/main/java/backupmanager/Controllers/EntryUserController.java +++ /dev/null @@ -1,20 +0,0 @@ -package backupmanager.Controllers; - -import javax.swing.JOptionPane; - -import backupmanager.Email.EmailValidator; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; - -public class EntryUserController { - public boolean isInputOkAndShowErrorIfNecessary(javax.swing.JDialog dialog, String name, String surname, String email) { - if (name.isEmpty() || surname.isEmpty() || email.isEmpty()) { - JOptionPane.showMessageDialog(dialog, TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_MISSING_DATA), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - return false; - } else if (!EmailValidator.isValidEmail(email)) { - JOptionPane.showMessageDialog(dialog, TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_WRONG_EMAIL), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - return false; - } - return true; - } -} diff --git a/src/main/java/backupmanager/Controllers/PreferenceController.java b/src/main/java/backupmanager/Controllers/PreferenceController.java deleted file mode 100644 index 5990480c..00000000 --- a/src/main/java/backupmanager/Controllers/PreferenceController.java +++ /dev/null @@ -1,27 +0,0 @@ -package backupmanager.Controllers; - -import java.io.IOException; -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.GUI.BackupManagerGUI; -import backupmanager.Managers.ExceptionManager; -import backupmanager.Services.PreferenceService; - -public record PreferenceController (PreferenceService service, BackupManagerGUI mainGui) { - private static final Logger logger = LoggerFactory.getLogger(PreferenceController.class); - - public void applyPreferences(String language, String theme) { - logger.info("Updating preferences -> Language: {}; Theme: ()", language, theme); - - try { - service.updatePreferences(language, theme); - mainGui.reloadPreferences(); - } catch (IOException ex) { - logger.error("An error occurred during applying preferences: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - } -} diff --git a/src/main/java/backupmanager/Controllers/TimePickerController.java b/src/main/java/backupmanager/Controllers/TimePickerController.java deleted file mode 100644 index 587c89cc..00000000 --- a/src/main/java/backupmanager/Controllers/TimePickerController.java +++ /dev/null @@ -1,56 +0,0 @@ -package backupmanager.Controllers; - -import javax.swing.JOptionPane; - -import backupmanager.Entities.TimeInterval; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; - -public class TimePickerController { - - private TimeInterval timeInterval; - private boolean closeOk; - - public TimePickerController(TimeInterval timeInterval, boolean closeOk) { - this.timeInterval = timeInterval; - this.closeOk = closeOk; - } - - public void handleOkButton(javax.swing.JDialog dialog, int days, int hours, int minutes) { - if (isLongTimeCorrect(days, hours, minutes)) { - if (isShortTimeCorrect(days, hours) && !showWarningMessageForShortTimeAndGetIfItOkayResponse(dialog)) - return; - - timeInterval = new TimeInterval(days, hours, minutes); - closeOk = true; - dialog.dispose(); - } - else - showErrorMessageForLongTime(dialog); - } - - public TimeInterval getTimeInterval() { - if (closeOk) return timeInterval; - return null; - } - public void setCloseOk(boolean closeOk) { this.closeOk = closeOk; } - - - private boolean isLongTimeCorrect(int days, int hours, int minutes) { - return days >= 0 && hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59 && - (days != 0 || hours != 0 || minutes != 0); - } - - private boolean isShortTimeCorrect(int days, int hours) { - return days == 0 && hours == 0; - } - - private void showErrorMessageForLongTime(javax.swing.JDialog dialog) { - JOptionPane.showMessageDialog(dialog, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_WRONG_TIME_INTERVAL), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - } - - private boolean showWarningMessageForShortTimeAndGetIfItOkayResponse(javax.swing.JDialog dialog) { - int response = JOptionPane.showConfirmDialog(dialog, TranslationCategory.DIALOGS.getTranslation(TranslationKey.WARNING_SHORT_TIME_INTERVAL_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.WARNING_GENERIC_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - return response == JOptionPane.YES_OPTION; - } -} diff --git a/src/main/java/backupmanager/Dialogs/BackupEntryDialog.form b/src/main/java/backupmanager/Dialogs/BackupEntryDialog.form deleted file mode 100644 index bfb184a5..00000000 --- a/src/main/java/backupmanager/Dialogs/BackupEntryDialog.form +++ /dev/null @@ -1,329 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/main/java/backupmanager/Dialogs/BackupEntryDialog.java b/src/main/java/backupmanager/Dialogs/BackupEntryDialog.java deleted file mode 100644 index 21f04ead..00000000 --- a/src/main/java/backupmanager/Dialogs/BackupEntryDialog.java +++ /dev/null @@ -1,537 +0,0 @@ -package backupmanager.Dialogs; - -import static backupmanager.GUI.BackupManagerGUI.backupTable; - -import java.time.LocalDateTime; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.formdev.flatlaf.FlatClientProperties; - -import backupmanager.BackupOperations; -import backupmanager.Controllers.BackupEntryController; -import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Entities.TimeInterval; -import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.Exceptions.BackupAlreadyRunningException; -import backupmanager.Exceptions.InvalidTimeInterval; -import backupmanager.Helpers.BackupHelper; -import backupmanager.Json.JSONConfigReader; - -public class BackupEntryDialog extends javax.swing.JDialog { - - private static final Logger logger = LoggerFactory.getLogger(BackupEntryDialog.class); - private static final JSONConfigReader configReader = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); - - private final boolean create; - private String backupOnText; - private String backupOffText; - - private final BackupEntryController entryController; - - public BackupEntryDialog(java.awt.Frame parent, boolean modal) { - super(parent, modal); - - entryController = new BackupEntryController(null); - - initializeDialog(); - setAutoBackupOff(); - this.create = true; - okButton.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.CREATE_BUTTON)); - } - - public BackupEntryDialog(java.awt.Frame parent, boolean modal, ConfigurationBackup currentBackup) { - super(parent, modal); - - entryController = new BackupEntryController(currentBackup); - - initializeDialog(); - updateCurrentFiedsByBackup(currentBackup); - backupNameField.setText(currentBackup.getName()); - backupNameField.setEditable(false); - backupNameField.setFocusable(false); - this.create = false; - okButton.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.SAVE_BUTTON)); - } - - private void setAutoBackupPreference(boolean option) { - ConfigurationBackup currentBackup = entryController.getCurrentBackup(); - currentBackup.setAutomatic(option); - - if (option) { - setAutoBackupOn(currentBackup); - } else { - disableAutoBackup(currentBackup); - } - } - - private void setStartPathField(String text) { - startPathField.setText(text); - } - private void setDestinationPathField(String text) { - destinationPathField.setText(text); - } - private void setCurrentBackupNotes(String notes) { - backupNoteTextArea.setText(notes); - } - private void setCurrentBackupMaxBackupsToKeep(int maxBackupsCount) { - maxBackupCountSpinner.setValue(maxBackupsCount); - } - - private void initializeDialog() { - initComponents(); - - setCurrentBackupMaxBackupsToKeep(configReader.getConfigValue("MaxCountForSameBackup", 1)); - - setSvgImages(); - setTranslations(); - } - - private void setLastBackupLabel(LocalDateTime date) { - if (date != null) { - String dateStr = date.format(BackupHelper.formatter); - dateStr = TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.LAST_BACKUP) + ": " + dateStr; - lastBackupLabel.setText(dateStr); - } - else lastBackupLabel.setText(""); - } - - private void openBackupActivationMessage(TimeInterval newtimeInterval) { - entryController.handleOpenBackupActivationMessage(newtimeInterval, startPathField.getText(), destinationPathField.getText()); - } - - private void updateCurrentFiedsByBackup(ConfigurationBackup backup) { - setStartPathField(backup.getTargetPath()); - setDestinationPathField(backup.getDestinationPath()); - setLastBackupLabel(backup.getLastUpdateDate()); - setAutoBackupPreference(backup.isAutomatic()); - setCurrentBackupNotes(backup.getNotes()); - setCurrentBackupMaxBackupsToKeep(backup.getMaxToKeep()); - - if (backup.getTimeIntervalBackup() != null) { - setAutoBackupOn(backup); - } else { - setAutoBackupOff(); - } - } - - private void toggleAutomaticBackup() { - if (entryController.toggleAutomaticBackup(backupNameField.getText(), startPathField.getText(), destinationPathField.getText(), backupNoteTextArea.getText(), toggleAutoBackup.isSelected(), (int) maxBackupCountSpinner.getValue())) { - setAutoBackupOn(entryController.getCurrentBackup()); - toggleAutoBackup.setSelected(true); - btnTimePicker.setToolTipText(entryController.getCurrentBackup().getTimeIntervalBackup().toString()); - btnTimePicker.setEnabled(true); - } else { - setAutoBackupOff(); - toggleAutoBackup.setSelected(false); - } - } - - private void setAutoBackupOn(ConfigurationBackup backup) { - toggleAutoBackup.setSelected(true); - toggleAutoBackup.setText(backupOnText); - - if (backup != null) - enableTimePickerButton(backup); - else - disableTimePickerButton(); - } - - private void setAutoBackupOff() { - toggleAutoBackup.setSelected(false); - toggleAutoBackup.setText(backupOffText); - disableTimePickerButton(); - } - - private void disableTimePickerButton() { - btnTimePicker.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.TIME_PICKER_TOOLTIP)); - btnTimePicker.setEnabled(false); - } - - private void enableTimePickerButton(ConfigurationBackup backup) { - if (backup.getTimeIntervalBackup() != null) { - btnTimePicker.setToolTipText(backup.getTimeIntervalBackup().toString()); - btnTimePicker.setEnabled(true); - } else { - btnTimePicker.setEnabled(true); - } - } - - private void disableAutoBackup(ConfigurationBackup backup) { - logger.info("Event --> auto backup disabled"); - - backup.setTimeIntervalBackup(null); - backup.setNextBackupDate(null); - backup.setAutomatic(false); - backup.setLastUpdateDate(LocalDateTime.now()); - } - - 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); - } - } - - private void setSvgImages() { - btnPathSearch1.setSvgImage("res/img/folder.svg", 30, 30); - btnPathSearch2.setSvgImage("res/img/folder.svg", 30, 30); - btnTimePicker.setSvgImage("res/img/timer.svg", 30, 30); - } - - private void setTranslations() { - backupOnText = TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.AUTO_BACKUP_BUTTON_ON); - backupOffText = TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.AUTO_BACKUP_BUTTON_OFF); - btnPathSearch1.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.INITIAL_FILE_CHOOSER_TOOLTIP)); - btnPathSearch2.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.DESTINATION_FILE_CHOOSER_TOOLTIP)); - startPathField.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.INITIAL_PATH_TOOLTIP)); - destinationPathField.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.DESTINATION_PATH_TOOLTIP)); - backupNoteTextArea.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.NOTES_TOOLTIP)); - singleBackup.setText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.SINGLE_BACKUP_BUTTON)); - singleBackup.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.SINGLE_BACKUP_TOOLTIP)); - toggleAutoBackup.setText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.AUTO_BACKUP_BUTTON_OFF)); - toggleAutoBackup.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.AUTO_BACKUP_TOOLTIP)); - jLabel2.setText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.NOTES) + ":"); - lastBackupLabel.setText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.LAST_BACKUP) + ": "); - setTitle(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.PAGE_TITLE)); - 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) + "\n" + TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.SPINNER_TOOLTIP)); - jLabel4.setText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.MAX_BACKUPS_TO_KEEP)); - closeButton.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.CLOSE_BUTTON)); - backupNameField.setToolTipText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.BACKUP_NAME_TOOLTIP)); - backupNameField.setHintText(TranslationCategory.BACKUP_ENTRY.getTranslation(TranslationKey.BACKUP_NAME)); - } - - /** - * 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. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - startPathField = new javax.swing.JTextField(); - btnPathSearch1 = new backupmanager.svg.SVGButton(); - btnPathSearch2 = new backupmanager.svg.SVGButton(); - destinationPathField = new javax.swing.JTextField(); - jLabel2 = new javax.swing.JLabel(); - jScrollPane2 = new javax.swing.JScrollPane(); - backupNoteTextArea = new javax.swing.JTextArea(); - lastBackupLabel = new javax.swing.JLabel(); - singleBackup = new javax.swing.JButton(); - toggleAutoBackup = new javax.swing.JToggleButton(); - btnTimePicker = new backupmanager.svg.SVGButton(); - maxBackupCountSpinner = new javax.swing.JSpinner(); - jLabel4 = new javax.swing.JLabel(); - closeButton = new javax.swing.JButton(); - okButton = new javax.swing.JButton(); - backupNameField = new backupmanager.customwidgets.ModernTextField(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setResizable(false); - - startPathField.setToolTipText("(Required) Initial path"); - startPathField.setActionCommand("null"); - startPathField.setAlignmentX(0.0F); - startPathField.setAlignmentY(0.0F); - startPathField.setAutoscrolls(false); - startPathField.setMaximumSize(new java.awt.Dimension(465, 26)); - startPathField.setMinimumSize(new java.awt.Dimension(465, 26)); - startPathField.setPreferredSize(new java.awt.Dimension(465, 26)); - - btnPathSearch1.setToolTipText(""); - btnPathSearch1.setMaximumSize(new java.awt.Dimension(35, 35)); - btnPathSearch1.setMinimumSize(new java.awt.Dimension(35, 35)); - btnPathSearch1.setPreferredSize(new java.awt.Dimension(35, 35)); - btnPathSearch1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnPathSearch1ActionPerformed(evt); - } - }); - - btnPathSearch2.setToolTipText("Open file explorer"); - btnPathSearch2.setMaximumSize(new java.awt.Dimension(35, 35)); - btnPathSearch2.setMinimumSize(new java.awt.Dimension(35, 35)); - btnPathSearch2.setPreferredSize(new java.awt.Dimension(35, 35)); - btnPathSearch2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnPathSearch2ActionPerformed(evt); - } - }); - - destinationPathField.setToolTipText("(Required) Destination path"); - destinationPathField.setActionCommand(""); - destinationPathField.setAlignmentX(0.0F); - destinationPathField.setAlignmentY(0.0F); - destinationPathField.setMaximumSize(new java.awt.Dimension(465, 26)); - destinationPathField.setMinimumSize(new java.awt.Dimension(465, 26)); - destinationPathField.setPreferredSize(new java.awt.Dimension(465, 26)); - - jLabel2.setText("notes:"); - - backupNoteTextArea.setColumns(20); - backupNoteTextArea.setRows(5); - backupNoteTextArea.setToolTipText("(Optional) Backup description"); - backupNoteTextArea.setMaximumSize(new java.awt.Dimension(232, 84)); - backupNoteTextArea.setMinimumSize(new java.awt.Dimension(232, 84)); - jScrollPane2.setViewportView(backupNoteTextArea); - - lastBackupLabel.setText("last backup: "); - - singleBackup.setBackground(new java.awt.Color(51, 153, 255)); - singleBackup.setForeground(new java.awt.Color(255, 255, 255)); - singleBackup.setText("Single Backup"); - singleBackup.setToolTipText("Perform the backup"); - singleBackup.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - singleBackup.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - SingleBackupActionPerformed(evt); - } - }); - - toggleAutoBackup.setText("Auto Backup"); - toggleAutoBackup.setToolTipText("Enable/Disable automatic backup"); - toggleAutoBackup.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - toggleAutoBackup.setPreferredSize(new java.awt.Dimension(108, 27)); - toggleAutoBackup.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - toggleAutoBackupActionPerformed(evt); - } - }); - - btnTimePicker.setToolTipText("Time picker"); - btnTimePicker.setMaximumSize(new java.awt.Dimension(36, 36)); - btnTimePicker.setMinimumSize(new java.awt.Dimension(36, 36)); - btnTimePicker.setPreferredSize(new java.awt.Dimension(36, 36)); - btnTimePicker.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnTimePickerActionPerformed(evt); - } - }); - - 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); - } - }); - - jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - jLabel4.setText("Keep only last"); - - closeButton.setText("Close"); - closeButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - closeButtonActionPerformed(evt); - } - }); - - okButton.setText("Ok"); - okButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - okButtonActionPerformed(evt); - } - }); - - backupNameField.setToolTipText("(Required) Backup name"); - backupNameField.setActionCommand("null"); - backupNameField.setAlignmentX(0.0F); - backupNameField.setAlignmentY(0.0F); - backupNameField.setAutoscrolls(false); - backupNameField.setMaximumSize(new java.awt.Dimension(465, 26)); - backupNameField.setMinimumSize(new java.awt.Dimension(465, 26)); - backupNameField.setPreferredSize(new java.awt.Dimension(465, 26)); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap(35, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(okButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(closeButton) - .addContainerGap()) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 469, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(destinationPathField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnPathSearch2, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 462, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lastBackupLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 461, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addGap(6, 6, 6) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(131, 131, 131) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(singleBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.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, 36, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(maxBackupCountSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(backupNameField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(startPathField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnPathSearch1, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(15, 15, 15)))) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(16, 16, 16) - .addComponent(backupNameField, javax.swing.GroupLayout.DEFAULT_SIZE, 53, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(startPathField, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(btnPathSearch1, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(destinationPathField, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(btnPathSearch2, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jLabel2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 190, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(lastBackupLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() - .addComponent(singleBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(toggleAutoBackup, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(btnTimePicker, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(maxBackupCountSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel4)) - .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(closeButton) - .addComponent(okButton)) - .addContainerGap()) - ); - - pack(); - setLocationRelativeTo(null); - }// //GEN-END:initComponents - - private void btnPathSearch1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPathSearch1ActionPerformed - logger.debug("File chooser: " + startPathField.getName() + ", files allowed: " + true); - String text = BackupOperations.pathSearchWithFileChooser(true); - if (text != null) { - startPathField.setText(text); - } - }//GEN-LAST:event_btnPathSearch1ActionPerformed - - private void btnPathSearch2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPathSearch2ActionPerformed - logger.debug("File chooser: " + destinationPathField.getName() + ", files allowed: " + false); - String text = BackupOperations.pathSearchWithFileChooser(false); - if (text != null) { - destinationPathField.setText(text); - } - }//GEN-LAST:event_btnPathSearch2ActionPerformed - - private void SingleBackupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_SingleBackupActionPerformed - try { - entryController.handleSingleBackupRequest( - backupTable, - backupNameField.getText(), - startPathField.getText(), - destinationPathField.getText(), - backupNoteTextArea.getText(), - toggleAutoBackup.isSelected(), - (int) maxBackupCountSpinner.getValue() - ); - } catch (BackupAlreadyRunningException e) { - // no handle - } - }//GEN-LAST:event_SingleBackupActionPerformed - - private void toggleAutoBackupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_toggleAutoBackupActionPerformed - logger.info("Event --> Changing auto backup preference"); - toggleAutomaticBackup(); - }//GEN-LAST:event_toggleAutoBackupActionPerformed - - private void btnTimePickerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTimePickerActionPerformed - try { - TimeInterval time = entryController.handleTimePickerAction(this, startPathField.getText(), destinationPathField.getText()); - btnTimePicker.setToolTipText(time.toString()); - openBackupActivationMessage(time); - } catch (InvalidTimeInterval e) { - // no actions - } - }//GEN-LAST:event_btnTimePickerActionPerformed - - 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 closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed - this.dispose(); - }//GEN-LAST:event_closeButtonActionPerformed - - private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - if (entryController.canDisposeAfterOk(backupNameField.getText(), startPathField.getText(), destinationPathField.getText(), backupNoteTextArea.getText(), toggleAutoBackup.isSelected(), (int) maxBackupCountSpinner.getValue(), create)) - this.dispose(); - }//GEN-LAST:event_okButtonActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton singleBackup; - private backupmanager.customwidgets.ModernTextField backupNameField; - private javax.swing.JTextArea backupNoteTextArea; - private backupmanager.svg.SVGButton btnPathSearch1; - private backupmanager.svg.SVGButton btnPathSearch2; - private backupmanager.svg.SVGButton btnTimePicker; - private javax.swing.JButton closeButton; - private javax.swing.JTextField destinationPathField; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel4; - private javax.swing.JScrollPane jScrollPane2; - private javax.swing.JLabel lastBackupLabel; - private javax.swing.JSpinner maxBackupCountSpinner; - private javax.swing.JButton okButton; - private javax.swing.JTextField startPathField; - private javax.swing.JToggleButton toggleAutoBackup; - // End of variables declaration//GEN-END:variables -} diff --git a/src/main/java/backupmanager/Dialogs/EntryUserDialog.form b/src/main/java/backupmanager/Dialogs/EntryUserDialog.form deleted file mode 100644 index 528982c2..00000000 --- a/src/main/java/backupmanager/Dialogs/EntryUserDialog.form +++ /dev/null @@ -1,122 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/main/java/backupmanager/Dialogs/EntryUserDialog.java b/src/main/java/backupmanager/Dialogs/EntryUserDialog.java deleted file mode 100644 index c8e1d78e..00000000 --- a/src/main/java/backupmanager/Dialogs/EntryUserDialog.java +++ /dev/null @@ -1,183 +0,0 @@ -package backupmanager.Dialogs; - - -import backupmanager.LimitDocument; -import backupmanager.Controllers.EntryUserController; -import backupmanager.Entities.User; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; - -public class EntryUserDialog extends javax.swing.JDialog { - - private User user; - private final EntryUserController userController; - - public EntryUserDialog(java.awt.Frame parent, boolean modal) { - super(parent, modal); - - userController = new EntryUserController(); - - initComponents(); - - nameTextField.setDocument(new LimitDocument(20)); - surnameTextField.setDocument(new LimitDocument(20)); - emailTextField.setDocument(new LimitDocument(32)); - - user = null; - - setTranslactions(); - } - - private void setTranslactions() { - setTitle(TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.USER_TITLE)); - nameLabel.setText(TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.USER_NAME)); - surnameLabel.setText(TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.USER_SURNAME)); - emailLabel.setText(TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.USER_EMAIL)); - } - - public User getUser() { - return user; - } - - /** - * 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. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - okBtn = new javax.swing.JButton(); - nameLabel = new javax.swing.JLabel(); - nameTextField = new javax.swing.JTextField(); - surnameLabel = new javax.swing.JLabel(); - surnameTextField = new javax.swing.JTextField(); - emailLabel = new javax.swing.JLabel(); - emailTextField = new javax.swing.JTextField(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle("Insert your data"); - setAlwaysOnTop(true); - setResizable(false); - - okBtn.setText("Ok"); - okBtn.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - okBtnActionPerformed(evt); - } - }); - - nameLabel.setText("Name"); - - nameTextField.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyReleased(java.awt.event.KeyEvent evt) { - nameTextFieldKeyReleased(evt); - } - }); - - surnameLabel.setText("Surname"); - - surnameTextField.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyReleased(java.awt.event.KeyEvent evt) { - surnameTextFieldKeyReleased(evt); - } - }); - - emailLabel.setText("Email"); - - emailTextField.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyReleased(java.awt.event.KeyEvent evt) { - emailTextFieldKeyReleased(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(emailLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(surnameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 81, Short.MAX_VALUE) - .addComponent(nameLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(0, 128, Short.MAX_VALUE) - .addComponent(okBtn)) - .addComponent(surnameTextField, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(emailTextField) - .addComponent(nameTextField)) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(nameLabel) - .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(surnameLabel) - .addComponent(surnameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 39, Short.MAX_VALUE) - .addComponent(okBtn) - .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(emailLabel) - .addComponent(emailTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) - ); - - pack(); - setLocationRelativeTo(null); - }// //GEN-END:initComponents - - private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed - String name = nameTextField.getText(); - String surname = surnameTextField.getText(); - String email = emailTextField.getText(); - - if (!userController.isInputOkAndShowErrorIfNecessary(this, name, surname, email)) - return; - - user = new User(name, surname, email); - - this.dispose(); - }//GEN-LAST:event_okBtnActionPerformed - - private void emailTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_emailTextFieldKeyReleased - if (evt.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { - okBtnActionPerformed(null); - } - }//GEN-LAST:event_emailTextFieldKeyReleased - - private void nameTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_nameTextFieldKeyReleased - if (evt.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { - surnameTextField.requestFocus(); - } - }//GEN-LAST:event_nameTextFieldKeyReleased - - private void surnameTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_surnameTextFieldKeyReleased - if (evt.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { - emailTextField.requestFocus(); - } - }//GEN-LAST:event_surnameTextFieldKeyReleased - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel emailLabel; - private javax.swing.JTextField emailTextField; - private javax.swing.JLabel nameLabel; - private javax.swing.JTextField nameTextField; - private javax.swing.JButton okBtn; - private javax.swing.JLabel surnameLabel; - private javax.swing.JTextField surnameTextField; - // End of variables declaration//GEN-END:variables -} diff --git a/src/main/java/backupmanager/Dialogs/PreferencesDialog.form b/src/main/java/backupmanager/Dialogs/PreferencesDialog.form deleted file mode 100644 index ca18cce3..00000000 --- a/src/main/java/backupmanager/Dialogs/PreferencesDialog.form +++ /dev/null @@ -1,124 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/main/java/backupmanager/Dialogs/PreferencesDialog.java b/src/main/java/backupmanager/Dialogs/PreferencesDialog.java deleted file mode 100644 index 8ad5e30e..00000000 --- a/src/main/java/backupmanager/Dialogs/PreferencesDialog.java +++ /dev/null @@ -1,182 +0,0 @@ -package backupmanager.Dialogs; - -import backupmanager.Controllers.GuiController; -import backupmanager.Controllers.PreferenceController; -import backupmanager.Entities.Confingurations; -import backupmanager.Enums.LanguagesEnum; -import backupmanager.Enums.ThemesEnum; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.Managers.ThemeManager; -import backupmanager.Services.PreferenceService; -import backupmanager.GUI.BackupManagerGUI; - - -public class PreferencesDialog extends javax.swing.JDialog { - - private final PreferenceController preferenceController; - - public PreferencesDialog(java.awt.Frame parent, boolean modal, BackupManagerGUI mainGui) { - super(parent, modal); - - preferenceController = new PreferenceController(new PreferenceService(), mainGui); - - initComponents(); - - this.setIconImage(GuiController.getIcon(this.getClass())); - - ThemeManager.updateThemeDialog(this); - - setLanguages(); - setThemes(); - setTranslations(); - } - - /** - * 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. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - languagesComboBox = new javax.swing.JComboBox<>(); - jLabel1 = new javax.swing.JLabel(); - jLabel2 = new javax.swing.JLabel(); - themesComboBox = new javax.swing.JComboBox<>(); - applyBtn = new javax.swing.JButton(); - closeBtn = new javax.swing.JButton(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle("Preferences"); - setAlwaysOnTop(true); - setResizable(false); - - languagesComboBox.setToolTipText(""); - - jLabel1.setText("Language"); - - jLabel2.setText("Theme"); - - themesComboBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - themesComboBoxActionPerformed(evt); - } - }); - - applyBtn.setText("Apply"); - applyBtn.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - applyBtnActionPerformed(evt); - } - }); - - closeBtn.setText("Close"); - closeBtn.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - closeBtnActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() - .addContainerGap(223, Short.MAX_VALUE) - .addComponent(applyBtn) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(closeBtn)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addGap(22, 22, 22) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 345, Short.MAX_VALUE) - .addComponent(themesComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(languagesComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) - .addContainerGap(13, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(20, 20, 20) - .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(languagesComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(themesComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 109, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(applyBtn) - .addComponent(closeBtn)) - .addGap(14, 14, 14)) - ); - - pack(); - setLocationRelativeTo(null); - }// //GEN-END:initComponents - - private void themesComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_themesComboBoxActionPerformed - - }//GEN-LAST:event_themesComboBoxActionPerformed - - private void applyBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyBtnActionPerformed - preferenceController.applyPreferences( - (String) languagesComboBox.getSelectedItem(), - (String) themesComboBox.getSelectedItem() - ); - setTranslations(); - ThemeManager.updateThemeDialog(this); - }//GEN-LAST:event_applyBtnActionPerformed - - private void closeBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeBtnActionPerformed - this.dispose(); - }//GEN-LAST:event_closeBtnActionPerformed - - private void setLanguages() { - languagesComboBox.addItem(LanguagesEnum.ENG.getLanguageName()); - languagesComboBox.addItem(LanguagesEnum.ITA.getLanguageName()); - languagesComboBox.addItem(LanguagesEnum.ESP.getLanguageName()); - languagesComboBox.addItem(LanguagesEnum.DEU.getLanguageName()); - languagesComboBox.addItem(LanguagesEnum.FRA.getLanguageName()); - - languagesComboBox.setSelectedItem(Confingurations.getLanguage().getLanguageName()); - } - - private void setThemes() { - themesComboBox.addItem(ThemesEnum.INTELLIJ.getThemeName()); - themesComboBox.addItem(ThemesEnum.DRACULA.getThemeName()); - themesComboBox.addItem(ThemesEnum.CARBON.getThemeName()); - themesComboBox.addItem(ThemesEnum.ARC_ORAGE.getThemeName()); - themesComboBox.addItem(ThemesEnum.ARC_DARK_ORANGE.getThemeName()); - themesComboBox.addItem(ThemesEnum.CYAN_LIGHT.getThemeName()); - themesComboBox.addItem(ThemesEnum.NORD.getThemeName()); - themesComboBox.addItem(ThemesEnum.HIGH_CONTRAST.getThemeName()); - themesComboBox.addItem(ThemesEnum.SOLARIZED_DARK.getThemeName()); - themesComboBox.addItem(ThemesEnum.SOLARIZED_LIGHT.getThemeName()); - - themesComboBox.setSelectedItem(Confingurations.getTheme().getThemeName()); - } - - private void setTranslations() { - setTitle(TranslationCategory.PREFERENCES_DIALOG.getTranslation(TranslationKey.PREFERENCES_TITLE)); - applyBtn.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.APPLY_BUTTON)); - closeBtn.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.CLOSE_BUTTON)); - jLabel1.setText(TranslationCategory.PREFERENCES_DIALOG.getTranslation(TranslationKey.LANGUAGE)); - jLabel2.setText(TranslationCategory.PREFERENCES_DIALOG.getTranslation(TranslationKey.THEME)); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton applyBtn; - private javax.swing.JButton closeBtn; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel2; - private javax.swing.JComboBox languagesComboBox; - private javax.swing.JComboBox themesComboBox; - // End of variables declaration//GEN-END:variables -} diff --git a/src/main/java/backupmanager/Dialogs/TimePicker.form b/src/main/java/backupmanager/Dialogs/TimePicker.form deleted file mode 100644 index 7ee92e1f..00000000 --- a/src/main/java/backupmanager/Dialogs/TimePicker.form +++ /dev/null @@ -1,187 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/main/java/backupmanager/Dialogs/TimePicker.java b/src/main/java/backupmanager/Dialogs/TimePicker.java deleted file mode 100644 index 7d13612a..00000000 --- a/src/main/java/backupmanager/Dialogs/TimePicker.java +++ /dev/null @@ -1,279 +0,0 @@ -package backupmanager.Dialogs; - -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.Controllers.GuiController; -import backupmanager.Entities.TimeInterval; - -import backupmanager.Controllers.TimePickerController; - -public class TimePicker extends javax.swing.JDialog { - - private final TimePickerController timePickerController; - - public TimePicker(java.awt.Dialog parent, TimeInterval timeInterval, boolean modal) { - super(parent, modal); - - timePickerController = new TimePickerController(timeInterval, false); - - initComponents(); - - if (timeInterval != null) { - daysSpinner.setValue(timeInterval.days()); - hoursSpinner.setValue(timeInterval.hours()); - minutesSpinner.setValue(timeInterval.minutes()); - } - - this.setIconImage(GuiController.getIcon(this.getClass())); - - setTranslations(); - } - - public TimeInterval getTimeInterval() { - return timePickerController.getTimeInterval(); - } - - private void daysIntervalSpinnerChange() { - Integer days = (Integer) daysSpinner.getValue(); - - if (days == null || days < 0) { - daysSpinner.setValue(0); - } - } - - private void hoursIntervalSpinnerChange() { - Integer hours = (Integer) hoursSpinner.getValue(); - - if (hours == null || hours < 0) { - hoursSpinner.setValue(0); - } else if (hours > 23) { - hoursSpinner.setValue(23); - } - } - - private void minutesIntervalSpinnerChange() { - Integer minutes = (Integer) minutesSpinner.getValue(); - - if (minutes == null || minutes < 0) { - minutesSpinner.setValue(0); - } else if (minutes > 59) { - minutesSpinner.setValue(59); - } - } - - 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); - } - } - - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jScrollPane1 = new javax.swing.JScrollPane(); - jTextArea1 = new javax.swing.JTextArea(); - jLabel1 = new javax.swing.JLabel(); - daysSpinner = new javax.swing.JSpinner(); - hoursSpinner = new javax.swing.JSpinner(); - jLabel2 = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); - minutesSpinner = new javax.swing.JSpinner(); - btnOk = new javax.swing.JButton(); - jButton2 = new javax.swing.JButton(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle("Time interval for auto backup"); - setMaximumSize(new java.awt.Dimension(325, 290)); - setMinimumSize(new java.awt.Dimension(325, 290)); - setPreferredSize(new java.awt.Dimension(325, 290)); - setResizable(false); - - jTextArea1.setEditable(false); - jTextArea1.setColumns(20); - jTextArea1.setRows(2); - jTextArea1.setText("Select how often to perform the automatic backup by \nchoosing the frequency in days, hours, and minutes."); - jTextArea1.setAutoscrolls(false); - jTextArea1.setBorder(null); - jTextArea1.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - jTextArea1.setFocusable(false); - jTextArea1.setRequestFocusEnabled(false); - jScrollPane1.setViewportView(jTextArea1); - - jLabel1.setText("Days"); - - daysSpinner.setToolTipText("Mouse wheel to adjust the value"); - daysSpinner.addChangeListener(new javax.swing.event.ChangeListener() { - public void stateChanged(javax.swing.event.ChangeEvent evt) { - daysSpinnerStateChanged(evt); - } - }); - daysSpinner.addMouseWheelListener(new java.awt.event.MouseWheelListener() { - public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { - daysSpinnerMouseWheelMoved(evt); - } - }); - - hoursSpinner.setToolTipText("Mouse wheel to adjust the value"); - hoursSpinner.addChangeListener(new javax.swing.event.ChangeListener() { - public void stateChanged(javax.swing.event.ChangeEvent evt) { - hoursSpinnerStateChanged(evt); - } - }); - hoursSpinner.addMouseWheelListener(new java.awt.event.MouseWheelListener() { - public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { - hoursSpinnerMouseWheelMoved(evt); - } - }); - - jLabel2.setText("Hours"); - - jLabel3.setText("Minutes"); - - minutesSpinner.setToolTipText("Mouse wheel to adjust the value"); - minutesSpinner.addChangeListener(new javax.swing.event.ChangeListener() { - public void stateChanged(javax.swing.event.ChangeEvent evt) { - minutesSpinnerStateChanged(evt); - } - }); - minutesSpinner.addMouseWheelListener(new java.awt.event.MouseWheelListener() { - public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { - minutesSpinnerMouseWheelMoved(evt); - } - }); - - btnOk.setText("Ok"); - btnOk.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnOkActionPerformed(evt); - } - }); - - jButton2.setText("Cancel"); - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .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() - .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(18, 18, 18) - .addComponent(daysSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(18, 18, 18) - .addComponent(hoursSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(minutesSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addComponent(btnOk)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton2))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(daysSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(hoursSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel2)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(minutesSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 87, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnOk) - .addComponent(jButton2)) - .addContainerGap()) - ); - - pack(); - setLocationRelativeTo(null); - }// //GEN-END:initComponents - - private void daysSpinnerMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_daysSpinnerMouseWheelMoved - mouseWeel(evt); - }//GEN-LAST:event_daysSpinnerMouseWheelMoved - - private void hoursSpinnerMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_hoursSpinnerMouseWheelMoved - mouseWeel(evt); - }//GEN-LAST:event_hoursSpinnerMouseWheelMoved - - private void minutesSpinnerMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_minutesSpinnerMouseWheelMoved - mouseWeel(evt); - }//GEN-LAST:event_minutesSpinnerMouseWheelMoved - - private void btnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOkActionPerformed - timePickerController.handleOkButton(this, (Integer) daysSpinner.getValue(), (Integer) hoursSpinner.getValue(), (Integer) minutesSpinner.getValue()); - }//GEN-LAST:event_btnOkActionPerformed - - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - timePickerController.setCloseOk(false); - this.dispose(); - }//GEN-LAST:event_jButton2ActionPerformed - - private void daysSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_daysSpinnerStateChanged - daysIntervalSpinnerChange(); - }//GEN-LAST:event_daysSpinnerStateChanged - - private void hoursSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_hoursSpinnerStateChanged - hoursIntervalSpinnerChange(); - }//GEN-LAST:event_hoursSpinnerStateChanged - - private void minutesSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_minutesSpinnerStateChanged - minutesIntervalSpinnerChange(); - }//GEN-LAST:event_minutesSpinnerStateChanged - - private void setTranslations() { - setTitle(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.TIME_INTERVAL_TITLE)); - jTextArea1.setText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.DESCRIPTION)); - daysSpinner.setToolTipText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.SPINNER_TOOLTIP)); - hoursSpinner.setToolTipText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.SPINNER_TOOLTIP)); - minutesSpinner.setToolTipText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.SPINNER_TOOLTIP)); - btnOk.setText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.OK_BUTTON)); - jButton2.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.CANCEL_BUTTON)); - jLabel1.setText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.DAYS)); - jLabel2.setText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.HOURS)); - jLabel3.setText(TranslationCategory.TIME_PICKER_DIALOG.getTranslation(TranslationKey.MINUTES)); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton btnOk; - private javax.swing.JSpinner daysSpinner; - private javax.swing.JSpinner hoursSpinner; - private javax.swing.JButton jButton2; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JTextArea jTextArea1; - private javax.swing.JSpinner minutesSpinner; - // End of variables declaration//GEN-END:variables -} diff --git a/src/main/java/backupmanager/Email/EmailSender.java b/src/main/java/backupmanager/Email/EmailSender.java index 95baf20f..966cab95 100644 --- a/src/main/java/backupmanager/Email/EmailSender.java +++ b/src/main/java/backupmanager/Email/EmailSender.java @@ -1,13 +1,14 @@ package backupmanager.Email; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.LocalDateTime; -import java.util.LinkedList; -import java.util.List; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,9 +17,9 @@ import backupmanager.Entities.User; import backupmanager.Enums.ConfigKey; import backupmanager.Enums.EmailType; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.Json.JSONConfigReader; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Json.JsonConfig; import backupmanager.database.Repositories.EmailRepository; import backupmanager.database.Repositories.UserRepository; import ch.qos.logback.classic.LoggerContext; @@ -29,7 +30,7 @@ */ public class EmailSender { - private static final JSONConfigReader configReader = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); + private static final JsonConfig configReader = JsonConfig.getInstance(); private static final Logger logger = LoggerFactory.getLogger(EmailSender.class); @@ -113,8 +114,8 @@ public static void sendUserCreationEmail(User user) { public static void sendConfirmEmailToUser(User user) { if (user == null) throw new IllegalArgumentException("User object cannot be null"); - String subject = TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.EMAIL_CONFIRMATION_SUBJECT); - String body = TranslationCategory.USER_DIALOG.getTranslation(TranslationKey.EMAIL_CONFIRMATION_BODY); + String subject = Translations.get(TKey.EMAIL_CONFIRMATION_SUBJECT); + String body = Translations.get(TKey.EMAIL_CONFIRMATION_BODY); body = body.replace("[UserName]", user.getUserCompleteName()); body = body.replace("[SupportEmail]", ConfigKey.EMAIL.getValue()); @@ -167,29 +168,33 @@ private static boolean hasWaitedSufficientTime(int minWaitDays, LocalDateTime no } private static String getTextFromLogFile(int rows) { - File file = new File(ConfigKey.LOG_DIRECTORY_STRING.getValue() + ConfigKey.LOG_FILE_STRING.getValue()); + Path file = Paths.get( + ConfigKey.LOG_DIRECTORY_STRING.getValue(), + ConfigKey.LOG_FILE_STRING.getValue() + ); - if (!file.exists() || !file.isFile() || file.length() == 0) { + if (!Files.exists(file) || !Files.isRegularFile(file)) { return "Log file does not exist or is empty."; } - List lastLines = new LinkedList<>(); - - try (BufferedReader reader = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) { - String line; + try { + Deque lastLines = new ArrayDeque<>(rows); - while ((line = reader.readLine()) != null) { - if (lastLines.size() == rows) { - lastLines.remove(0); // remove the older - } - lastLines.add(line); + try (Stream stream = Files.lines(file, StandardCharsets.UTF_8)) { + stream.forEach(line -> { + if (lastLines.size() == rows) { + lastLines.removeFirst(); + } + lastLines.addLast(line); + }); } + + return String.join("\n", lastLines); + } catch (IOException e) { - logger.error("An error occurred during reading the log file for getting the last rows: " + e.getMessage(), e); + logger.error("Error reading log file: " + e.getMessage(), e); return "Error reading the log file."; } - - return String.join("\n", lastLines); } private static User getCurrentUser() { diff --git a/src/main/java/backupmanager/Entities/BackupAnalyticsSnapshot.java b/src/main/java/backupmanager/Entities/BackupAnalyticsSnapshot.java new file mode 100644 index 00000000..b8e91b02 --- /dev/null +++ b/src/main/java/backupmanager/Entities/BackupAnalyticsSnapshot.java @@ -0,0 +1,26 @@ +package backupmanager.Entities; + +import java.time.LocalDate; +import java.util.Map; + +public record BackupAnalyticsSnapshot( + long totalRequests, + long successCount, + long failedCount, + double successRate, + double avgDurationMs, + double avgCompressionRate, + long totalDiskUsageBytes, + Map durationTrend +) { + public static BackupAnalyticsSnapshot emptyDataset() { + return new BackupAnalyticsSnapshot( + 0, 0, 0, + 0, + 0, + 0, + 0, + Map.of() + ); + } +} diff --git a/src/main/java/backupmanager/Entities/BackupExecutionContext.java b/src/main/java/backupmanager/Entities/BackupExecutionContext.java new file mode 100644 index 00000000..e6e20754 --- /dev/null +++ b/src/main/java/backupmanager/Entities/BackupExecutionContext.java @@ -0,0 +1,13 @@ +package backupmanager.Entities; + +import backupmanager.Utils.FolderUtils; + +public record BackupExecutionContext ( + ConfigurationBackup backup, + long folderUnzippedSize +) { + public static BackupExecutionContext create(ConfigurationBackup backup) { + long folderSize = FolderUtils.calculateFileOrFolderSize(backup.getTargetPath()); + return new BackupExecutionContext(backup, folderSize); + } +} diff --git a/src/main/java/backupmanager/Entities/BackupUIContext.java b/src/main/java/backupmanager/Entities/BackupUIContext.java new file mode 100644 index 00000000..d78a0b64 --- /dev/null +++ b/src/main/java/backupmanager/Entities/BackupUIContext.java @@ -0,0 +1,16 @@ +package backupmanager.Entities; + +import java.awt.TrayIcon; + +import javax.swing.JMenuItem; + +import backupmanager.gui.Table.BackupTableDataService; +import backupmanager.gui.frames.BackupProgressGUI; + +public record BackupUIContext ( + TrayIcon trayIcon, + BackupTableDataService backupTableService, + BackupProgressGUI progressBar, + JMenuItem interruptBackupPopupItem, + JMenuItem deleteBackupPopupItem +) { } diff --git a/src/main/java/backupmanager/Entities/ConfigurationBackup.java b/src/main/java/backupmanager/Entities/ConfigurationBackup.java index fffdff2a..33e1c383 100644 --- a/src/main/java/backupmanager/Entities/ConfigurationBackup.java +++ b/src/main/java/backupmanager/Entities/ConfigurationBackup.java @@ -3,12 +3,11 @@ import java.time.LocalDateTime; import java.util.List; -import backupmanager.Enums.ConfigKey; -import backupmanager.Json.JSONConfigReader; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; import backupmanager.database.Repositories.BackupConfigurationRepository; public class ConfigurationBackup { - private static final JSONConfigReader configReader = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); private int id; private String name; @@ -24,22 +23,6 @@ public class ConfigurationBackup { private int count; private int maxToKeep; - public ConfigurationBackup() { - id = 0; - name = ""; - targetPath = ""; - destinationPath = ""; - lastBackupDate = null; - automatic = false; - nextBackupDate = null; - timeIntervalBackup = null; - notes = ""; - creationDate = LocalDateTime.now(); - lastUpdateDate = LocalDateTime.now(); - count = 0; - maxToKeep = configReader.getConfigValue("MaxCountForSameBackup", 1); - } - public ConfigurationBackup(String name, String targetPath, String destinationPath, LocalDateTime lastBackupDate, Boolean automatic, LocalDateTime nextBackupDate, TimeInterval timeIntervalBackup, String notes, LocalDateTime creationDate, LocalDateTime lastUpdateDate, int count, int maxToKeep) { this.name = name; this.targetPath = targetPath; @@ -120,6 +103,19 @@ public String toCsvString() { ); } + public Object[] toTableRow() { + return new Object[] { + name, + targetPath, + destinationPath, + lastBackupDate, + automatic, + nextBackupDate, + timeIntervalBackup, + maxToKeep + }; + } + public static ConfigurationBackup getBackupByName(List backups, String name) { for (ConfigurationBackup backup : backups) { if (backup.getName().equals(name)) { @@ -137,103 +133,43 @@ public static String getCSVHeader() { return "BackupName,targetPath,DestinationPath,lastBackupDate,IsAutoBackup,NextDate,Interval (gg.HH:mm),MaxBackupsToKeep"; } - public int getId() { - return id; - } - - public String getName() { - return name; - } - - public String getTargetPath() { - return targetPath; - } - - public String getDestinationPath() { - return destinationPath; - } - - public LocalDateTime getLastBackupDate() { - return lastBackupDate; - } - - public boolean isAutomatic() { - return automatic; - } - - public LocalDateTime getNextBackupDate() { - return nextBackupDate; - } - - public TimeInterval getTimeIntervalBackup() { - return timeIntervalBackup; - } - - public String getNotes() { - return notes; - } - - public LocalDateTime getCreationDate() { - return creationDate; - } - - public LocalDateTime getLastUpdateDate() { - return lastUpdateDate; - } - - public int getCount() { - return count; - } - - public int getMaxToKeep() { - return maxToKeep; - } - - public void setName(String name) { - this.name = name; - } - - public void setNotes(String notes) { - this.notes = notes; - } - - public void setAutomatic(boolean automatic) { - this.automatic = automatic; - } - - public void setCreationDate(LocalDateTime creationDate) { - this.creationDate = creationDate; - } - - public void setDestinationPath(String destinationPath) { - this.destinationPath = destinationPath; - } - - public void setLastBackupDate(LocalDateTime lastBackupDate) { - this.lastBackupDate = lastBackupDate; - } - - public void setLastUpdateDate(LocalDateTime lastUpdateDate) { - this.lastUpdateDate = lastUpdateDate; - } - - public void setNextBackupDate(LocalDateTime nextBackupDate) { - this.nextBackupDate = nextBackupDate; - } - - public void setTargetPath(String targetPath) { - this.targetPath = targetPath; - } - - public void setTimeIntervalBackup(TimeInterval timeIntervalBackup) { - this.timeIntervalBackup = timeIntervalBackup; - } - - public void setMaxToKeep(int maxToKeep) { - this.maxToKeep = maxToKeep; - } - - public void setCount(int count) { - this.count = count; - } + public static String[] getCSVHeaderArray() { + return new String[] { + Translations.get(TKey.BACKUP_NAME_COLUMN), + Translations.get(TKey.INITIAL_PATH_COLUMN), + Translations.get(TKey.DESTINATION_PATH_COLUMN), + Translations.get(TKey.LAST_BACKUP_COLUMN), + Translations.get(TKey.AUTOMATIC_BACKUP_COLUMN), + Translations.get(TKey.NEXT_BACKUP_DATE_COLUMN), + Translations.get(TKey.TIME_INTERVAL_COLUMN), + Translations.get(TKey.MAX_BACKUPS_COLUMN) + }; + } + + public int getId() { return id; } + public String getName() { return name; } + public String getTargetPath() { return targetPath; } + public String getDestinationPath() { return destinationPath; } + public LocalDateTime getLastBackupDate() { return lastBackupDate; } + public boolean isAutomatic() { return automatic; } + public LocalDateTime getNextBackupDate() { return nextBackupDate; } + public TimeInterval getTimeIntervalBackup() { return timeIntervalBackup; } + public String getNotes() { return notes; } + public LocalDateTime getCreationDate() { return creationDate; } + public LocalDateTime getLastUpdateDate() { return lastUpdateDate; } + public int getCount() { return count; } + public int getMaxToKeep() { return maxToKeep; } + + public void setName(String name) { this.name = name; } + public void setNotes(String notes) { this.notes = notes; } + public void setAutomatic(boolean automatic) { this.automatic = automatic; } + public void setCreationDate(LocalDateTime creationDate) { this.creationDate = creationDate; } + public void setDestinationPath(String destinationPath) { this.destinationPath = destinationPath; } + public void setLastBackupDate(LocalDateTime lastBackupDate) { this.lastBackupDate = lastBackupDate; } + public void setLastUpdateDate(LocalDateTime lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } + public void setNextBackupDate(LocalDateTime nextBackupDate) { this.nextBackupDate = nextBackupDate; } + public void setTargetPath(String targetPath) { this.targetPath = targetPath; } + public void setTimeIntervalBackup(TimeInterval timeIntervalBackup) { this.timeIntervalBackup = timeIntervalBackup; } + public void setMaxToKeep(int maxToKeep) { this.maxToKeep = maxToKeep; } + public void setCount(int count) { this.count = count; } } diff --git a/src/main/java/backupmanager/Entities/Configurations.java b/src/main/java/backupmanager/Entities/Configurations.java new file mode 100644 index 00000000..448b09e5 --- /dev/null +++ b/src/main/java/backupmanager/Entities/Configurations.java @@ -0,0 +1,28 @@ +package backupmanager.Entities; + +import backupmanager.database.Repositories.ConfigurationRepository; + +public class Configurations { + private static boolean subscriptionNedded; // if true the subscription is needed to use the backuground service + + public static void loadAllConfigurations() { + setSubscriptionNedded(ConfigurationRepository.getConfigurationValueByCode("SubscriptionNedded")); + } + + // i don't want to update the subscription value from the code. for now the only method is doing manually + public static void updateAllConfigurations() { + } + + public static void setSubscriptionNedded(boolean isNedded) { + subscriptionNedded = isNedded; + } + + private static void setSubscriptionNedded(String subscriptionValue) { + subscriptionValue = subscriptionValue.trim().toLowerCase(); + subscriptionNedded = subscriptionValue.equals("true") || subscriptionValue.equals("1"); + } + + public static boolean isSubscriptionNedded() { + return subscriptionNedded; + } +} diff --git a/src/main/java/backupmanager/Entities/Confingurations.java b/src/main/java/backupmanager/Entities/Confingurations.java deleted file mode 100644 index b2c6795c..00000000 --- a/src/main/java/backupmanager/Entities/Confingurations.java +++ /dev/null @@ -1,102 +0,0 @@ -package backupmanager.Entities; - -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.Enums.LanguagesEnum; -import backupmanager.Enums.ThemesEnum; -import backupmanager.Managers.ExceptionManager; -import backupmanager.database.Repositories.ConfigurationRepository; - -public class Confingurations { - private static final Logger logger = LoggerFactory.getLogger(Confingurations.class); - private static LanguagesEnum language; - private static ThemesEnum theme; - private static boolean subscriptionNedded; // if true the subscription is needed to use the backuground service - - public static void loadAllConfigurations() { - setLanguageByFileName(ConfigurationRepository.getConfigurationValueByCode("Language")); - setTheme(ConfigurationRepository.getConfigurationValueByCode("Theme")); - setSubscriptionNedded(ConfigurationRepository.getConfigurationValueByCode("SubscriptionNedded")); - } - - // i don't want to update the subscription value from the code. for now the only method is doing manually - public static void updateAllConfigurations() { - ConfigurationRepository.updateConfigurationValueByCode("Language", language.getFileName()); - ConfigurationRepository.updateConfigurationValueByCode("Theme", theme.getThemeName()); - } - - public static void setLanguage(LanguagesEnum language) { - Confingurations.language = language; - } - - public static void setLanguageByLanguageName(String selectedLanguage) { - try { - for (LanguagesEnum lang : LanguagesEnum.values()) { - if (lang.getLanguageName().equalsIgnoreCase(selectedLanguage)) { - language = lang; - logger.info("Language setted to: " + language.getLanguageName()); - return; - } - } - logger.warn("Invalid language name: " + selectedLanguage); - } catch (Exception ex) { - logger.error("An error occurred during setting language operation: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - } - - private static void setLanguageByFileName(String filename) { - try { - for (LanguagesEnum lang : LanguagesEnum.values()) { - if (lang.getFileName().equalsIgnoreCase(filename)) { - language = lang; - logger.info("Language setted to: " + language.getLanguageName()); - return; - } - } - logger.warn("Invalid file name: " + filename); - } catch (Exception ex) { - logger.error("An error occurred during setting language operation: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - } - - public static void setTheme(ThemesEnum theme) { - Confingurations.theme = theme; - } - public static void setTheme(String selectedTheme) { - try { - for (ThemesEnum t : ThemesEnum.values()) { - if (t.getThemeName().equalsIgnoreCase(selectedTheme)) { - theme = t; - logger.info("Theme set to: " + theme.getThemeName()); - return; - } - } - logger.warn("Invalid theme name: " + selectedTheme); - } catch (Exception ex) { - logger.error("An error occurred during setting theme operation: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - } - - private static void setSubscriptionNedded(String subscriptionValue) { - subscriptionValue = subscriptionValue.trim().toLowerCase(); - subscriptionNedded = subscriptionValue.equals("true") || subscriptionValue.equals("1"); - } - - public static LanguagesEnum getLanguage() { - return language; - } - - public static ThemesEnum getTheme() { - return theme; - } - - public static boolean isSubscriptionNedded() { - return subscriptionNedded; - } -} diff --git a/src/main/java/backupmanager/Entities/TimeInterval.java b/src/main/java/backupmanager/Entities/TimeInterval.java index 52c5a467..b91905e2 100644 --- a/src/main/java/backupmanager/Entities/TimeInterval.java +++ b/src/main/java/backupmanager/Entities/TimeInterval.java @@ -18,11 +18,11 @@ public String toString() { } public static TimeInterval getTimeIntervalFromString(String time) { - if (time != null && !time.matches("\\d+\\.\\d{1,2}:\\d{1,2}")) { + if (time != null && !time.matches("\\d+\\.\\d{1,2}:\\d{1,2}")) throw new IllegalArgumentException("Invalid time format. Expected format: days.hours:minutes (e.g., 1.12:30)"); - } - if (time == null) return null; + if (time == null) + return null; String[] dayAndTime = time.split("\\."); int parsedDays = Integer.parseInt(dayAndTime[0]); @@ -31,15 +31,12 @@ public static TimeInterval getTimeIntervalFromString(String time) { int parsedHours = Integer.parseInt(hourAndMinute[0]); int parsedMinutes = Integer.parseInt(hourAndMinute[1]); - if (parsedDays < 0) { + if (parsedDays < 0) throw new IllegalArgumentException("Days cannot be negative"); - } - if (parsedHours < 0 || parsedHours > 23) { + if (parsedHours < 0 || parsedHours > 23) throw new IllegalArgumentException("Hours must be between 0 and 23"); - } - if (parsedMinutes < 0 || parsedMinutes > 59) { + if (parsedMinutes < 0 || parsedMinutes > 59) throw new IllegalArgumentException("Minutes must be between 0 and 59"); - } return new TimeInterval(parsedDays, parsedHours, parsedMinutes); } diff --git a/src/main/java/backupmanager/Entities/User.java b/src/main/java/backupmanager/Entities/User.java index 51bc1cad..d0c8875b 100644 --- a/src/main/java/backupmanager/Entities/User.java +++ b/src/main/java/backupmanager/Entities/User.java @@ -8,10 +8,6 @@ public User(String name, String surname, String email) { this(0, name, surname, email, Locale.getDefault().getDisplayName()); } - public User(int id, String name, String surname, String email) { - this(id, name, surname, email, Locale.getDefault().getDisplayName()); - } - public String getUserCompleteName() { return name + " " + surname; } @@ -20,8 +16,4 @@ public String getUserCompleteName() { public String toString() { return name + " " + surname + ", " + email + ", " + language; } - - public static User getDefaultUser() { - return new User("Unregistered", "User", ""); - } } diff --git a/src/main/java/backupmanager/Entities/ZippingContext.java b/src/main/java/backupmanager/Entities/ZippingContext.java index f0ac5706..6962c6ba 100644 --- a/src/main/java/backupmanager/Entities/ZippingContext.java +++ b/src/main/java/backupmanager/Entities/ZippingContext.java @@ -1,24 +1,6 @@ package backupmanager.Entities; -import java.awt.TrayIcon; - -import javax.swing.JMenuItem; - -import backupmanager.GUI.BackupProgressGUI; -import backupmanager.Table.BackupTable; -import backupmanager.Utils.FolderUtils; - public record ZippingContext ( - ConfigurationBackup backup, - TrayIcon trayIcon, - BackupTable backupTable, - BackupProgressGUI progressBar, - JMenuItem interruptBackupPopupItem, - JMenuItem deleteBackupPopupItem, - long folderUnzippedSize -) { - public static ZippingContext create(ConfigurationBackup backup, TrayIcon trayIcon, BackupTable backupTable, BackupProgressGUI progressBar, JMenuItem interruptBackupPopupItem, JMenuItem deleteBackupPopupItem) { - long folderSize = FolderUtils.calculateFileOrFolderSize(backup.getTargetPath()); - return new ZippingContext(backup, trayIcon, backupTable, progressBar, interruptBackupPopupItem, deleteBackupPopupItem, folderSize); - } -} + BackupExecutionContext execution, + BackupUIContext ui +) { } diff --git a/src/main/java/backupmanager/Enums/ConfigKey.java b/src/main/java/backupmanager/Enums/ConfigKey.java index 1eaf542a..1edcb24b 100644 --- a/src/main/java/backupmanager/Enums/ConfigKey.java +++ b/src/main/java/backupmanager/Enums/ConfigKey.java @@ -29,7 +29,10 @@ public enum ConfigKey { SHARE_LINK, VERSION, GUI_WIDTH, - GUI_HEIGHT; + GUI_HEIGHT, + GUI_MIN_WIDTH, + GUI_MIN_HEIGHT, + ; private static final Map configValues = new EnumMap<>(ConfigKey.class); private static final Logger logger = LoggerFactory.getLogger(ConfigKey.class); diff --git a/src/main/java/backupmanager/Enums/LanguagesEnum.java b/src/main/java/backupmanager/Enums/LanguagesEnum.java index ac0ba615..92aa7b69 100644 --- a/src/main/java/backupmanager/Enums/LanguagesEnum.java +++ b/src/main/java/backupmanager/Enums/LanguagesEnum.java @@ -17,8 +17,12 @@ public String getLanguageName() { return languageName; } + public static LanguagesEnum getDefault() { + return ENG; + } + private LanguagesEnum(String languageName, String fileName) { this.languageName = languageName; this.fileName = fileName; } -} \ No newline at end of file +} diff --git a/src/main/java/backupmanager/Enums/MenuItems.java b/src/main/java/backupmanager/Enums/MenuItems.java index 5da1dd8b..e735c2be 100644 --- a/src/main/java/backupmanager/Enums/MenuItems.java +++ b/src/main/java/backupmanager/Enums/MenuItems.java @@ -2,20 +2,19 @@ public enum MenuItems { BugReport, - Preferences, - Clear, + Settings, Donate, PaypalDonate, BuymeacoffeeDonate, History, InfoPage, - New, - Quit, - Save, Import, Export, - SaveWithName, - Share, Support, - Website + ContactUs, + Website, + BackupList, + Dashboard, + About, + Subscription } diff --git a/src/main/java/backupmanager/Enums/SubscriptionStatus.java b/src/main/java/backupmanager/Enums/SubscriptionStatus.java new file mode 100644 index 00000000..bfcc8967 --- /dev/null +++ b/src/main/java/backupmanager/Enums/SubscriptionStatus.java @@ -0,0 +1,8 @@ +package backupmanager.Enums; + +public enum SubscriptionStatus { + EXPIRED, + ACTIVE, + EXPIRATION, + NONE // No subscription needed +} diff --git a/src/main/java/backupmanager/Enums/ThemesEnum.java b/src/main/java/backupmanager/Enums/ThemesEnum.java deleted file mode 100644 index 774ab6b8..00000000 --- a/src/main/java/backupmanager/Enums/ThemesEnum.java +++ /dev/null @@ -1,24 +0,0 @@ -package backupmanager.Enums; - -public enum ThemesEnum { - INTELLIJ("Light"), - DRACULA("Dark"), - CARBON("Carbon"), - ARC_ORAGE("Arc - Orange"), - ARC_DARK_ORANGE("Arc Dark - Orange"), - CYAN_LIGHT("Cyan light"), - NORD("Nord"), - HIGH_CONTRAST("High contrast"), - SOLARIZED_DARK("Solarized dark"), - SOLARIZED_LIGHT("Solarized light"); - - private final String themeName; - - public String getThemeName() { - return themeName; - } - - private ThemesEnum(String themeName) { - this.themeName = themeName; - } -} \ No newline at end of file diff --git a/src/main/java/backupmanager/Enums/TranslationLoaderEnum.java b/src/main/java/backupmanager/Enums/TranslationLoaderEnum.java deleted file mode 100644 index 7eebce77..00000000 --- a/src/main/java/backupmanager/Enums/TranslationLoaderEnum.java +++ /dev/null @@ -1,340 +0,0 @@ -package backupmanager.Enums; - -import java.io.FileReader; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -public class TranslationLoaderEnum { - - private static final Logger logger = LoggerFactory.getLogger(TranslationLoaderEnum.class); - - public enum TranslationCategory { - GENERAL("General"), - MENU("Menu"), - TABBED_FRAMES("TabbedFrames"), - BACKUP_ENTRY("BackupEntry"), - BACKUP_LIST("BackupList"), - TIME_PICKER_DIALOG("TimePickerDialog"), - PREFERENCES_DIALOG("PreferencesDialog"), - USER_DIALOG("UserDialog"), - PROGRESS_BACKUP_FRAME("ProgressBackupFrame"), - TRAY_ICON("TrayIcon"), - DIALOGS("Dialogs"), - SUBSCRIPTION("Subscription"); - - private final String categoryName; - private final Map translations = new HashMap<>(); - - TranslationCategory(String categoryName) { - this.categoryName = categoryName; - } - - public void addTranslation(TranslationKey key, String value) { - translations.put(key, value); - } - - // Updated getTranslation method - public String getTranslation(TranslationKey key) { - return translations.getOrDefault(key, key.getDefaultValue()); - } - - public String getCategoryName() { - return categoryName; - } - } - - public enum TranslationKey { - // General - APP_NAME("AppName", "Backup Manager"), - VERSION("Version", "Version"), - BACKUP("Backup", "Backup"), - FROM("From", "From"), - TO("To", "To"), - CLOSE_BUTTON("CloseButton", "Close"), - OK_BUTTON("OkButton", "Ok"), - CANCEL_BUTTON("CancelButton", "Cancel"), - APPLY_BUTTON("ApplyButton", "Apply"), - SAVE_BUTTON("SaveButton", "Save"), - CREATE_BUTTON("CreateButton", "Create"), - - // Menu - FILE("File", "File"), - OPTIONS("Options", "Options"), - ABOUT("About", "About"), - HELP("Help", "Help"), - BUG_REPORT("BugReport", "Report a bug"), - CLEAR("Clear", "Clear"), - DONATE("Donate", "Donate"), - HISTORY("History", "History"), - INFO_PAGE("InfoPage", "Info"), - 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"), - WEBSITE("Website", "Website"), - - // TabbedFrames - BACKUP_ENTRY("BackupEntry", "Backup Entry"), - BACKUP_LIST("BackupList", "Backup List"), - - // BackupEntry - PAGE_TITLE("PageTitle", "Backup Entry"), - CURRENT_FILE("CurrentFile", "Current file"), - NOTES("Notes", "Notes"), - LAST_BACKUP("LastBackup", "Last backup"), - SINGLE_BACKUP_BUTTON("SingleBackupButton", "Single Backup"), - AUTO_BACKUP_BUTTON("AutoBackupButton", "Auto Backup"), - AUTO_BACKUP_BUTTON_ON("AutoBackupButtonON", "Auto Backup (ON)"), - AUTO_BACKUP_BUTTON_OFF("AutoBackupButtonOFF", "Auto Backup (OFF)"), - INITIAL_PATH_PLACEHOLDER("InitialPathPlaceholder", "Initial path"), - DESTINATION_PATH_PLACEHOLDER("DestinationPathPlaceholder", "Destination path"), - BACKUP_NAME("BackupName", "Backup name"), - BACKUP_NAME_TOOLTIP("BackupNameTooltip", "(Required) Backup name"), - INITIAL_PATH_TOOLTIP("InitialPathTooltip", "(Required) Initial path"), - DESTINATION_PATH_TOOLTIP("DestinationPathTooltip", "(Required) Destination path"), - INITIAL_FILE_CHOOSER_TOOLTIP("InitialFileChooserTooltip", "Open file explorer"), - DESTINATION_FILE_CHOOSER_TOOLTIP("DestinationFileChooserTooltip", "Open file explorer"), - NOTES_TOOLTIP("NotesTooltip", "(Optional) Backup description"), - 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"), - INITIAL_PATH_COLUMN("InitialPathColumn", "Initial Path"), - DESTINATION_PATH_COLUMN("DestinationPathColumn", "Destination Path"), - LAST_BACKUP_COLUMN("LastBackupColumn", "Last Backup"), - AUTOMATIC_BACKUP_COLUMN("AutomaticBackupColumn", "Automatic Backup"), - NEXT_BACKUP_DATE_COLUMN("NextBackupDateColumn", "Next Backup Date"), - TIME_INTERVAL_COLUMN("TimeIntervalColumn", "Time Interval"), - BACKUP_NAME_DETAIL("BackupNameDetail", "BackupName"), - INITIAL_PATH_DETAIL("InitialPathDetail", "InitialPath"), - DESTINATION_PATH_DETAIL("DestinationPathDetail", "DestinationPath"), - LAST_BACKUP_DETAIL("LastBackupDetail", "LastBackup"), - NEXT_BACKUP_DATE_DETAIL("NextBackupDateDetail", "NextBackup"), - TIME_INTERVAL_DETAIL("TimeIntervalDetail", "TimeInterval"), - CREATION_DATE_DETAIL("CreationDateDetail", "CreationDate"), - 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"), - EXPORT_AS("ExportAs", "Export as: "), - EXPORT_AS_PDF_TOOLTIP("ExportAsPdfTooltip", "Export as PDF"), - EXPORT_AS_CSV_TOOLTIP("ExportAsCsvTooltip", "Export as CSV"), - RESEARCH_BAR_TOOLTIP("ResearchBarTooltip", "Research bar"), - RESEARCH_BAR_PLACEHOLDER("ResearchBarPlaceholder", "Search..."), - EDIT_POPUP("EditPopup", "Edit"), - DELETE_POPUP("DeletePopup", "Delete"), - INTERRUPT_POPUP("InterruptPopup", "Interrupt"), - DUPLICATE_POPUP("DuplicatePopup", "Duplicate"), - RENAME_BACKUP_POPUP("RenameBackupPopup", "Rename backup"), - OPEN_INITIAL_FOLDER_POPUP("OpenInitialFolderPopup", "Open initial path"), - OPEN_DESTINATION_FOLDER_POPUP("OpenDestinationFolderPopup", "Open destination path"), - BACKUP_POPUP("BackupPopup", "Backup"), - SINGLE_BACKUP_POPUP("SingleBackupPopup", "Run single backup"), - AUTO_BACKUP_POPUP("AutoBackupPopup", "Auto backup"), - COPY_TEXT_POPUP("CopyTextPopup", "Copy text"), - COPY_BACKUP_NAME_POPUP("CopyBackupNamePopup", "Copy backup name"), - COPY_INITIAL_PATH_POPUP("CopyInitialPathPopup", "Copy initial path"), - COPY_DESTINATION_PATH_BACKUP("CopyDestinationPathPopup", "Copy destination path"), - - // TimePickerDialog - TIME_INTERVAL_TITLE("TimeIntervalTitle", "Time interval for auto backup"), - DESCRIPTION("Description", "Select how often to perform the automatic backup by choosing the frequency in days, hours, and minutes."), - DAYS("Days", "Days"), - HOURS("Hours", "Hours"), - MINUTES("Minutes", "Minutes"), - SPINNER_TOOLTIP("SpinnerTooltip", "Mouse wheel to adjust the value"), - - // PreferencesDialog - PREFERENCES_TITLE("PreferencesTitle", "Preferences"), - LANGUAGE("Language", "Language"), - THEME("Theme", "Theme"), - - // User dialog - USER_TITLE("UserTitle", "Insert your data"), - USER_NAME("Name", "Name"), - USER_SURNAME("Surname", "Surname"), - USER_EMAIL("Email", "Email"), - ERROR_MESSAGE_FOR_MISSING_DATA("ErrorMessageForMissingData", "Please fill in all the required fields."), - ERROR_MESSAGE_FOR_WRONG_EMAIL("ErrorMessageForWrongEmail", "The provided email address is invalid. Please provide a correct one."), - EMAIL_CONFIRMATION_SUBJECT("EmailConfirmationSubject", "Thank you for choosing Backup Manager!"), - EMAIL_CONFIRMATION_BODY("EmailConfirmationBody", "Hi [UserName],\n\nThank you for downloading and registering **Backup Manager**, your new tool for secure and efficient backup management!\n\nThis is an automated email sent to confirm your registration. We will contact you by email only to inform you about new releases or important updates of the application.\n\nIn the meantime, if you have any questions, need assistance, or have suggestions, we are always here for you. You can reach us at **[SupportEmail]**.\n\nThank you again for choosing Backup Manager, and enjoy managing your backups!\n\nBest regards,\nThe Backup Manager Team"), - - // ProgressBackupFrame - PROGRESS_BACKUP_TITLE("ProgressBackupTitle", "Backup in progress"), - STATUS_COMPLETED("StatusCompleted", "Backup completed!"), - STATUS_LOADING("StatusLoading", "Loading..."), - - // TrayIcon - TRAY_TOOLTIP("TrayTooltip", "Backup Service"), - OPEN_ACTION("OpenAction", "Quick Access"), - EXIT_ACTION("ExitAction", "Exit"), - SUCCESS_MESSAGE("SuccessMessage", "\nThe backup was successfully completed:"), - ERROR_MESSAGE_INPUT_MISSING("ErrorMessageInputMissing", "\nError during automatic backup.\nInput Missing!"), - ERROR_MESSAGE_FILES_NOT_EXISTING("ErrorMessageFilesNotExisting", "\nError during automatic backup.\nOne or both paths do not exist!"), - ERROR_MESSAGE_SAME_PATHS("ErrorMessageSamePaths", "\nError during automatic backup.\nThe initial path and destination path cannot be the same. Please choose different paths!"), - - // Dialogs - ERROR_GENERIC_TITLE("ErrorGenericTitle", "Error"), - WARNING_GENERIC_TITLE("WarningGenericTitle", "Warning"), - WARNING_BACKUP_ALREADY_IN_PROGRESS_MESSAGE("WarningBackupAlreadyInProgressMessage", "There is already a backup in progress. It is not possible to perform parallel backups"), - WARNING_SHORT_TIME_INTERVAL_MESSAGE("WarningShortTimeIntervalMessage", "The selected time interval is very short. For optimal performance, we recommend setting it to at least one hour. Do you still want to proceed?"), - - ERROR_MESSAGE_FOR_FOLDER_NOT_EXISTING("ErrorMessageForFolderNotExisting", "The folder does not exist or is invalid"), - ERROR_MESSAGE_FOR_SAVING_FILE_WITH_PATHS_EMPTY("ErrorMessageForSavingFileWithPathsEmpty", "Unable to save the file. Both the initial and destination paths must be specified and cannot be empty"), - BACKUP_SAVED_CORRECTLY_TITLE("BackupSavedCorrectlyTitle", "Backup saved"), - BACKUP_SAVED_CORRECTLY_MESSAGE("BackupSavedCorrectlyMessage", "saved successfully!"), - ERROR_SAVING_BACKUP_MESSAGE("ErrorSavingBackupMessage", "Error saving backup"), - 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..."), - EXCEPTION_MESSAGE_CLIPBOARD_MESSAGE("ExceptionMessageClipboardMessage", "Error text has been copied to the clipboard."), - EXCEPTION_MESSAGE_CLIPBOARD_BUTTON("ExceptionMessageClipboardButton", "Copy to clipboard"), - EXCEPTION_MESSAGE_REPORT_BUTTON("ExceptionMessageReportButton", "Report the Problem"), - EXCEPTION_MESSAGE_REPORT_MESSAGE("ExceptionMessageReportMessage", "Please report this error, either with an image of the screen or by copying the following error text (it is appreciable to provide a description of the operations performed before the error):"), - ERROR_MESSAGE_OPENING_WEBSITE("ErrorMessageOpeningWebsite", "Failed to open the web page. Please try again."), - CONFIRMATION_MESSAGE_FOR_CLEAR("ConfirmationMessageForClear", "Are you sure you want to clean the fields?"), - CONFIRMATION_MESSAGE_FOR_UNSAVED_CHANGES("ConfirmationMessageForUnsavedChanges", "There are unsaved changes, do you want to save them before moving to another backup?"), - ERROR_MESSAGE_OPEN_HISTORY_FILE("ErrorMessageOpenHistoryFile", "Error opening history file."), - CONFIRMATION_MESSAGE_BEFORE_DELETE_BACKUP("ConfirmationMessageBeforeDeleteBackup", "Are you sure you want to delete this item? Please note, this action cannot be undone"), - SHARE_LINK_COPIED_MESSAGE("ShareLinkCopiedMessage", "Share link copied to clipboard!"), - CONFIRMATION_MESSAGE_CANCEL_AUTO_BACKUP("ConfirmationMessageCancelAutoBackup", "Are you sure you want to cancel automatic backups for this entry?"), - CONFIRMATION_MESSAGE_CANCEL_SINGLE_BACKUP("ConfirmationMessageCancelSingleBackup", "Are you sure you want to cancel this backup?"), - CONFIRMATION_MESSAGE_BEFORE_EXIT("ConfirmationMessageBeforeExit", "Are you sure you want to exit?"), - ERROR_MESSAGE_UNEXPECTED("ErrorMessageUnexpected", "An unexpected error has occurred!"), - ERROR_MESSAGE_PATHS_CANNOT_BE_SAME("ErrorMessagePathsCannotBeSame", "The initial path and destination path cannot be the same!"), - ERROR_MESSAGE_PATHS_ARE_EMPTY("ErrorMessagePathsAreEmpty", "The initial path and destination path cannot be empty!"), - ERROR_MESSAGE_INVALID_PATH("ErrorMessageInvalidPath", "The selected path is invalid!"), - ERROR_MESSAGE_NOT_SUPPORTED_EMAIL("ErrorMessageNotSupportedEmail", "Your system does not support sending emails directly from this application."), - ERROR_MESSAGE_NOT_SUPPORTED_EMAIL_GENERIC("ErrorMessageNotSupportedEmailGeneric", "Your system does not support sending emails."), - ERROR_WRONG_TIME_INTERVAL("ErrorWrongTimeInterval", "The time interval is not correct"), - AUTO_BACKUP_ACTIVATED_MESSAGE("AutoBackupActivatedMessage", "Auto Backup has been activated"), - SETTED_EVERY_MESSAGE("SettedEveryMessage", "\nIs setted every"), - DAYS_MESSAGE("DaysMessage", " days"), - ERROR_MESSAGE_UNABLE_TO_SEND_EMAIL("ErrorMessageUnableToSendEmail", "Unable to send email. Please try again later."), - INTERRUPT_BACKUP_PROCESS_MESSAGE("InterruptBackupProcessMessage", "Are you sure you want to stop this backup?"), - ERROR_MESSAGE_INPUT_MISSING_GENERIC("ErrorMessageInputMissingGeneric", "Input Missing!"), - 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."), - ERROR_MESSAGE_COUNTING_FILES("ErrorMessageCountingFiles", "Error occurred while calculating files to back up."), - ERROR_MESSAGE_ZIPPING_GENERIC("ErrorMessageZippingGeneric", "Error occurred while zipping files."), - ERROR_MESSAGE_ZIPPING_IO("ErrorMessageZippingIO", "Error occurred while zipping files: I/O error."), - ERROR_MESSAGE_ZIPPING_SECURITY("ErrorMessageZippingSecurity", "Error occurred while zipping files: Security error."), - SUCCESS_GENERIC_TITLE( "SuccessGenericTitle", "Success"), - SUCCESSFULLY_EXPORTED_TO_CSV_MESSAGE("SuccessfullyExportedToCsvMessage", "Backups exported to CSV successfully!"), - SUCCESSFULLY_EXPORTED_TO_PDF_MESSAGE("SuccessfullyExportedToPdfMessage", "Backups exported to PDF successfully!"), - ERROR_MESSAGE_FOR_EXPORTING_TO_CSV("ErrorMessageForExportingToCsv", "Error exporting backups to CSV: "), - ERROR_MESSAGE_FOR_EXPORTING_TO_PDF("ErrorMessageForExportingToPdf", "Error exporting backups to PDF: "), - CSV_NAME_MESSAGE_INPUT("CsvNameMessageInput", "Enter the name of the CSV file."), - PDF_NAME_MESSAGE_INPUT("PdfNameMessageInput", "Enter the name of the PDF file."), - DUPLICATED_FILE_NAME_MESSAGE("DuplicatedFileNameMessage", "File already exists. Overwrite?"), - ERROR_MESSAGE_INVALID_FILENAME("ErrorMessageInvalidFilename", "Invalid file name. Use only alphanumeric characters, dashes, and underscores."), - CONFIRMATION_DELETION_TITLE("ConfirmationDeletionTitle", "Confirm Deletion"), - CONFIRMATION_DELETION_MESSAGE("ConfirmationDeletionMessage", "Are you sure you want to delete the selected rows?"), - - // InfoPage - INFO_PAGE_DESCRIPTION("InfoPageDescription", "Backup automatic system for files with the option to schedule and make backups regularly."), - INFO_PAGE_FEATURES_TITLE("InfoPageFeaturesTitle", "Features"), - INFO_PAGE_FEATURES_DESCRIPTION("InfoPageFeaturesDescription", "Backup files quickly and easily with automatic scheduling and periodic backups"), - INFO_PAGE_BUTTON_TITLE("InfoPageButtonTitle", "OK"), - INFO_PAGE_VERSION("InfoPageVersion", "Version: "), - INFO_PAGE_DEVELOPER("InfoPageDeveloper", "Developer: "), - INFO_PAGE_CREDITS("InfoPageCredits", "Credits"), - INFO_PAGE_LICENSE("InfoPageLicense", "License"), - - // Subscription - SUBSCRIPTION_EXPIRING_TITLE("ExpiringTitle", "Backup Manager subscription expiring soon"), - SUBSCRIPTION_EXPIRING_MESSAGE("ExpiringMessage", "Your Backup Manager subscription is about to expire.\nAutomatic backups will continue to run until the expiration date.\nPlease contact support to renew it."), - SUBSCRIPTION_EXPIRED_TITLE("ExpiredTitle", "Backup Manager subscription expired"), - SUBSCRIPTION_EXPIRED_MESSAGE("ExpiredMessage", "Your Backup Manager subscription has expired.\nAutomatic backups will no longer run.\nPlease contact support to reactivate it."); - - private final String keyName; - private final String defaultValue; - - private static final Map lookup = new HashMap<>(); - - static { - for (TranslationKey key : TranslationKey.values()) { - lookup.put(key.keyName, key); - } - } - - // Constructor to assign both key and default value - private TranslationKey(String keyName, String defaultValue) { - this.keyName = keyName; - this.defaultValue = defaultValue; - } - - // Lookup by keyName (JSON key) - public static TranslationKey fromKeyName(String keyName) { - return lookup.get(keyName); - } - - public String getKeyName() { - return keyName; - } - - public String getDefaultValue() { - return defaultValue; - } - } - - public static void loadTranslations(String filePath) throws IOException { - Gson gson = new Gson(); - - try (FileReader reader = new FileReader(filePath, StandardCharsets.UTF_8)) { - JsonObject jsonObject = gson.fromJson(reader, JsonObject.class); - - for (TranslationCategory category : TranslationCategory.values()) { - JsonObject categoryTranslations = jsonObject.getAsJsonObject(category.getCategoryName()); - - if (categoryTranslations != null) { - for (Map.Entry entry : categoryTranslations.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue().getAsString(); - - // Use fromKeyName to get the TranslationKey from the JSON key - TranslationKey translationKey = TranslationKey.fromKeyName(key); - if (translationKey != null) { - // If value is null or empty, fall back to the default value from the enum - String translationValue = (value != null && !value.isEmpty()) ? value : translationKey.getDefaultValue(); - category.addTranslation(translationKey, translationValue); - } else { - logger.warn("Unrecognized key in JSON: " + key + ", using default value"); - } - } - } - } - } catch (Exception ex) { - logger.error("An error occurred: " + ex.getMessage(), ex); - } - } -} diff --git a/src/main/java/backupmanager/Enums/Translations.java b/src/main/java/backupmanager/Enums/Translations.java new file mode 100644 index 00000000..fcc62077 --- /dev/null +++ b/src/main/java/backupmanager/Enums/Translations.java @@ -0,0 +1,416 @@ +package backupmanager.Enums; + +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import backupmanager.Managers.LanguageManager; + +public class Translations { + + private static final Logger logger = LoggerFactory.getLogger(Translations.class); + + public enum TCategory { + GENERAL("General"), + MENU("Menu"), + BACKUP_ENTRY("BackupEntry"), + BACKUP_LIST("BackupList"), + TIME_PICKER_DIALOG("TimePickerDialog"), + USER_DIALOG("UserDialog"), + PROGRESS_BACKUP_FRAME("ProgressBackupFrame"), + TRAY_ICON("TrayIcon"), + DIALOGS("Dialogs"), + SUBSCRIPTION("Subscription"), + HISTORY_LOGS("HistoryLogs"), + ABOUT("About"), + DASHBOARD("Dashboard"), + SETTINGS("Settings"), + SEARCH_BAR("SearchBar"), + TOAST("Toast"), + ; + + private final String categoryName; + private final Map translations = new HashMap<>(); + + TCategory(String categoryName) { + this.categoryName = categoryName; + } + + public void addTranslation(TKey key, String value) { + translations.put(key, value); + } + + // Updated getTranslation method + public String getTranslation(TKey key) { + return translations.getOrDefault(key, key.getDefaultValue()); + } + + public String getCategoryName() { + return categoryName; + } + + public void clearTranslations() { + translations.clear(); + } + } + + public enum TKey { + // General + APP_NAME(TCategory.GENERAL, "AppName", "Backup Manager"), + VERSION(TCategory.GENERAL, "Version", "Version"), + BACKUP(TCategory.GENERAL, "Backup", "Backup"), + FROM(TCategory.GENERAL, "From", "From"), + TO(TCategory.GENERAL, "To", "To"), + CLOSE_BUTTON(TCategory.GENERAL, "CloseButton", "Close"), + OK_BUTTON(TCategory.GENERAL, "OkButton", "Ok"), + CANCEL_BUTTON(TCategory.GENERAL, "CancelButton", "Cancel"), + APPLY_BUTTON(TCategory.GENERAL, "ApplyButton", "Apply"), + SAVE_BUTTON(TCategory.GENERAL, "SaveButton", "Save"), + CREATE_BUTTON(TCategory.GENERAL, "CreateButton", "Create"), + EDIT_BUTTON(TCategory.GENERAL, "EditButton", "Edit"), + DELETE_BUTTON(TCategory.GENERAL, "DeleteButton", "Delete"), + CONTACT_US(TCategory.GENERAL, "ContactUs", "Contact us"), + QUICK_SEARCH(TCategory.GENERAL, "QuickSearch", "Quick Search..."), + + // Menu + SUBMENU_MAIN(TCategory.MENU, "SubmenuMain", "MAIN"), + SUBMENU_OTHER(TCategory.MENU, "SubmenuOther", "OTHER"), + FILE(TCategory.MENU, "File", "File"), + OPTIONS(TCategory.MENU, "Options", "Options"), + ABOUT(TCategory.MENU, "About", "About"), + HELP(TCategory.MENU, "Help", "Help"), + BUG_REPORT(TCategory.MENU, "BugReport", "Report a bug"), + DONATE(TCategory.MENU, "Donate", "Support the project"), + HISTORY(TCategory.MENU, "History", "History"), + NEW(TCategory.MENU, "New", "New"), + BACKUP_TABLE(TCategory.MENU, "Backups", "Backup List"), + IMPORT_BACKUP(TCategory.MENU, "ImportBackup", "Import backups from Csv"), + EXPORT_BACKUP(TCategory.MENU, "ExportBackup", "Export backups to Csv"), + DASHBOARD(TCategory.MENU, "Dashboard", "Dashboard"), + GITHUB_PAGE(TCategory.MENU, "Github", "Github page"), + PAYPAL(TCategory.MENU, "Paypal", "Paypal"), + BUYMEACOFFE(TCategory.MENU, "BuyMeACoffe", "Buy me a coffe"), + SUBSCRIPTION(TCategory.MENU, "Subscription", "Subscription"), + + // BackupEntry + PAGE_SUBTITLE_CREATE(TCategory.BACKUP_ENTRY, "PageSubtitleCreate", "Create Backup"), + PAGE_SUBTITLE_EDIT(TCategory.BACKUP_ENTRY, "PageSubtitleEdit", "Edit Backup"), + PAGE_SUBTITLE_INFO(TCategory.BACKUP_ENTRY, "PageSubtitleInfo", "Backup Information"), + PAGE_SUBTITLE_SETTINGS(TCategory.BACKUP_ENTRY, "PageSubtitleSettings", "Backups Settings"), + NOTES(TCategory.BACKUP_ENTRY, "Notes", "Notes"), + PATHS(TCategory.BACKUP_ENTRY, "Paths", "Paths"), + LAST_BACKUP(TCategory.BACKUP_ENTRY, "LastBackup", "Last backup"), + SINGLE_BACKUP_BUTTON(TCategory.BACKUP_ENTRY, "SingleBackupButton", "Single Backup"), + AUTO_BACKUP_BUTTON_ON(TCategory.BACKUP_ENTRY, "AutoBackupButtonON", "Auto Backup (ON)"), + AUTO_BACKUP_BUTTON_OFF(TCategory.BACKUP_ENTRY, "AutoBackupButtonOFF", "Auto Backup (OFF)"), + BACKUP_NAME_PLACEHOLDER(TCategory.BACKUP_ENTRY, "BackupNamePlaceholder", "Backup name (unique)"), + INITIAL_PATH_PLACEHOLDER(TCategory.BACKUP_ENTRY, "InitialPathPlaceholder", "Target path e.g. C:\\Users\\Admin\\Documents"), + DESTINATION_PATH_PLACEHOLDER(TCategory.BACKUP_ENTRY, "DestinationPathPlaceholder", "Destination folder e.g. D:\\Backups"), + BACKUP_NAME(TCategory.BACKUP_ENTRY, "BackupName", "Backup name"), + BACKUP_NAME_TOOLTIP(TCategory.BACKUP_ENTRY, "BackupNameTooltip", "(Required) Backup name"), + INITIAL_PATH_TOOLTIP(TCategory.BACKUP_ENTRY, "InitialPathTooltip", "(Required) Initial path"), + DESTINATION_PATH_TOOLTIP(TCategory.BACKUP_ENTRY, "DestinationPathTooltip", "(Required) Destination path"), + INITIAL_FILE_CHOOSER_TOOLTIP(TCategory.BACKUP_ENTRY, "InitialFileChooserTooltip", "Open file explorer"), + DESTINATION_FILE_CHOOSER_TOOLTIP(TCategory.BACKUP_ENTRY, "DestinationFileChooserTooltip", "Open file explorer"), + NOTES_TOOLTIP(TCategory.BACKUP_ENTRY, "NotesTooltip", "(Optional) Backup description"), + SINGLE_BACKUP_TOOLTIP(TCategory.BACKUP_ENTRY, "SingleBackupTooltip", "Perform the backup"), + AUTO_BACKUP_TOOLTIP(TCategory.BACKUP_ENTRY, "AutoBackupTooltip", "Enable/Disable automatic backup"), + TIME_PICKER_TOOLTIP(TCategory.BACKUP_ENTRY, "TimePickerTooltip", "Time picker"), + MAX_BACKUPS_TO_KEEP(TCategory.BACKUP_ENTRY, "MaxBackupsToKeep", "Max backups to keep"), + MAX_BACKUPS_TO_KEEP_TOOLTIP(TCategory.BACKUP_ENTRY, "MaxBackupsToKeepTooltip", "Maximum number of backups before removing the oldest."), + + // BackupList + BACKUP_LIST_TITLE(TCategory.BACKUP_LIST, "BackupListTitle", "Backup List"), + BACKUP_LIST_DESCRIPTION(TCategory.BACKUP_LIST, "BackupListDescription", "Manage and monitor backup configurations, including creation, editing, scheduling, and execution."), + BACKUP_NAME_COLUMN(TCategory.BACKUP_LIST, "BackupNameColumn", "Backup Name"), + INITIAL_PATH_COLUMN(TCategory.BACKUP_LIST, "InitialPathColumn", "Initial Path"), + DESTINATION_PATH_COLUMN(TCategory.BACKUP_LIST, "DestinationPathColumn", "Destination Path"), + LAST_BACKUP_COLUMN(TCategory.BACKUP_LIST, "LastBackupColumn", "Last Backup"), + AUTOMATIC_BACKUP_COLUMN(TCategory.BACKUP_LIST, "AutomaticBackupColumn", "Automatic Backup"), + NEXT_BACKUP_DATE_COLUMN(TCategory.BACKUP_LIST, "NextBackupDateColumn", "Next Backup Date"), + TIME_INTERVAL_COLUMN(TCategory.BACKUP_LIST, "TimeIntervalColumn", "Interval (gg.HH:mm)"), + MAX_BACKUPS_COLUMN(TCategory.BACKUP_LIST, "MaxBackupsColumn", "Max Backups To Keep"), + BACKUP_NAME_DETAIL(TCategory.BACKUP_LIST, "BackupNameDetail", "BackupName"), + INITIAL_PATH_DETAIL(TCategory.BACKUP_LIST, "InitialPathDetail", "InitialPath"), + DESTINATION_PATH_DETAIL(TCategory.BACKUP_LIST, "DestinationPathDetail", "DestinationPath"), + LAST_BACKUP_DETAIL(TCategory.BACKUP_LIST, "LastBackupDetail", "LastBackup"), + NEXT_BACKUP_DATE_DETAIL(TCategory.BACKUP_LIST, "NextBackupDateDetail", "NextBackup"), + TIME_INTERVAL_DETAIL(TCategory.BACKUP_LIST, "TimeIntervalDetail", "TimeInterval"), + CREATION_DATE_DETAIL(TCategory.BACKUP_LIST, "CreationDateDetail", "CreationDate"), + LAST_UPDATE_DATE_DETAIL(TCategory.BACKUP_LIST, "LastUpdateDateDetail", "LastUpdateDate"), + BACKUP_COUNT_DETAIL(TCategory.BACKUP_LIST, "BackupCountDetail", "BackupCount"), + NOTES_DETAIL(TCategory.BACKUP_LIST, "NotesDetail", "Notes"), + MAX_BACKUPS_TO_KEEP_DETAIL(TCategory.BACKUP_LIST, "MaxBackupsToKeepDetail", "MaxBackupsToKeep"), + RESEARCH_BAR_TOOLTIP(TCategory.BACKUP_LIST, "ResearchBarTooltip", "Research bar"), + RESEARCH_BAR_PLACEHOLDER(TCategory.BACKUP_LIST, "ResearchBarPlaceholder", "Search..."), + EDIT_POPUP(TCategory.BACKUP_LIST, "EditPopup", "Edit"), + DELETE_POPUP(TCategory.BACKUP_LIST, "DeletePopup", "Delete"), + INTERRUPT_POPUP(TCategory.BACKUP_LIST, "InterruptPopup", "Interrupt"), + DUPLICATE_POPUP(TCategory.BACKUP_LIST, "DuplicatePopup", "Duplicate"), + RENAME_BACKUP_POPUP(TCategory.BACKUP_LIST, "RenameBackupPopup", "Rename backup"), + OPEN_INITIAL_FOLDER_POPUP(TCategory.BACKUP_LIST, "OpenInitialFolderPopup", "Open initial path"), + OPEN_DESTINATION_FOLDER_POPUP(TCategory.BACKUP_LIST, "OpenDestinationFolderPopup", "Open destination path"), + BACKUP_POPUP(TCategory.BACKUP_LIST, "BackupPopup", "Backup"), + SINGLE_BACKUP_POPUP(TCategory.BACKUP_LIST, "SingleBackupPopup", "Run single backup"), + AUTO_BACKUP_POPUP(TCategory.BACKUP_LIST, "AutoBackupPopup", "Auto backup"), + COPY_TEXT_POPUP(TCategory.BACKUP_LIST, "CopyTextPopup", "Copy text"), + COPY_BACKUP_NAME_POPUP(TCategory.BACKUP_LIST, "CopyBackupNamePopup", "Copy backup name"), + COPY_INITIAL_PATH_POPUP(TCategory.BACKUP_LIST, "CopyInitialPathPopup", "Copy initial path"), + COPY_DESTINATION_PATH_BACKUP(TCategory.BACKUP_LIST, "CopyDestinationPathPopup", "Copy destination path"), + + // TimePickerDialog + TIME_INTERVAL_TITLE(TCategory.TIME_PICKER_DIALOG, "TimeIntervalTitle", "Time interval for auto backup"), + DESCRIPTION(TCategory.TIME_PICKER_DIALOG, "Description", "Select how often to perform the automatic backup by choosing the frequency in days, hours, and minutes."), + DAYS(TCategory.TIME_PICKER_DIALOG, "Days", "Days"), + HOURS(TCategory.TIME_PICKER_DIALOG, "Hours", "Hours"), + MINUTES(TCategory.TIME_PICKER_DIALOG, "Minutes", "Minutes"), + SPINNER_TOOLTIP(TCategory.TIME_PICKER_DIALOG, "SpinnerTooltip", "Mouse wheel to adjust the value"), + + // User dialog + USER_TITLE(TCategory.USER_DIALOG, "UserTitle", "Insert your data"), + USER_DESCRIPTION(TCategory.USER_DIALOG, "UserDescription", "Please enter your data to access the system"), + USER_NAME(TCategory.USER_DIALOG, "Name", "Name"), + USER_SURNAME(TCategory.USER_DIALOG, "Surname", "Surname"), + USER_EMAIL(TCategory.USER_DIALOG, "Email", "Email"), + USER_NAME_PLACEHOLDER(TCategory.USER_DIALOG, "UserNamePlaceholder", "Enter your name"), + USER_SURNAME_PLACEHOLDER(TCategory.USER_DIALOG, "UserSurnamePlaceholder", "Enter your surname"), + USER_EMAIL_PLACEHOLDER(TCategory.USER_DIALOG, "UserEmailPlaceholder", "Enter your email"), + EMAIL_CONFIRMATION_SUBJECT(TCategory.USER_DIALOG, "EmailConfirmationSubject", "Thank you for choosing Backup Manager!"), + EMAIL_CONFIRMATION_BODY(TCategory.USER_DIALOG, "EmailConfirmationBody", "Hi [UserName],\n\nThank you for downloading and registering **Backup Manager**, your new tool for secure and efficient backup management!\n\nThis is an automated email sent to confirm your registration. We will contact you by email only to inform you about new releases or important updates of the application.\n\nIn the meantime, if you have any questions, need assistance, or have suggestions, we are always here for you. You can reach us at **[SupportEmail]**.\n\nThank you again for choosing Backup Manager, and enjoy managing your backups!\n\nBest regards,\nThe Backup Manager Team"), + + // ProgressBackupFrame + PROGRESS_BACKUP_TITLE(TCategory.PROGRESS_BACKUP_FRAME, "ProgressBackupTitle", "Backup in progress"), + STATUS_COMPLETED(TCategory.PROGRESS_BACKUP_FRAME, "StatusCompleted", "Backup completed!"), + STATUS_LOADING(TCategory.PROGRESS_BACKUP_FRAME, "StatusLoading", "Loading..."), + + // TrayIcon + TRAY_TOOLTIP(TCategory.TRAY_ICON, "TrayTooltip", "Backup Service"), + OPEN_ACTION(TCategory.TRAY_ICON, "OpenAction", "Quick Access"), + EXIT_ACTION(TCategory.TRAY_ICON, "ExitAction", "Exit"), + SUCCESS_MESSAGE(TCategory.TRAY_ICON, "SuccessMessage", "\nThe backup was successfully completed:"), + ERROR_MESSAGE_INPUT_MISSING(TCategory.TRAY_ICON, "ErrorMessageInputMissing", "\nError during automatic backup.\nInput Missing!"), + ERROR_MESSAGE_FILES_NOT_EXISTING(TCategory.TRAY_ICON, "ErrorMessageFilesNotExisting", "\nError during automatic backup.\nOne or both paths do not exist!"), + ERROR_MESSAGE_SAME_PATHS(TCategory.TRAY_ICON, "ErrorMessageSamePaths", "\nError during automatic backup.\nThe initial path and destination path cannot be the same. Please choose different paths!"), + + // Dialogs + ERROR_GENERIC_TITLE(TCategory.DIALOGS, "ErrorGenericTitle", "Error"), + WARNING_GENERIC_TITLE(TCategory.DIALOGS, "WarningGenericTitle", "Warning"), + WARNING_BACKUP_ALREADY_IN_PROGRESS_MESSAGE(TCategory.DIALOGS, "WarningBackupAlreadyInProgressMessage", "There is already a backup in progress. It is not possible to perform parallel backups"), + WARNING_SHORT_TIME_INTERVAL_MESSAGE(TCategory.DIALOGS, "WarningShortTimeIntervalMessage", "The selected time interval is very short. For optimal performance, we recommend setting it to at least one hour. Do you still want to proceed?"), + + BACKUP_NAME_INPUT(TCategory.DIALOGS, "BackupNameInput", "Name of the backup"), + CONFIRMATION_REQUIRED_TITLE(TCategory.DIALOGS, "ConfirmationRequiredTitle", "Confirmation required"), + DUPLICATED_BACKUP_NAME_MESSAGE(TCategory.DIALOGS, "DuplicatedBackupNameMessage", "A backup with the same name already exists, do you want to overwrite it?"), + EXCEPTION_MESSAGE_CLIPBOARD_BUTTON(TCategory.DIALOGS, "ExceptionMessageClipboardButton", "Copy to clipboard"), + EXCEPTION_MESSAGE_REPORT_BUTTON(TCategory.DIALOGS, "ExceptionMessageReportButton", "Report the Problem"), + EXCEPTION_MESSAGE_REPORT_MESSAGE(TCategory.DIALOGS, "ExceptionMessageReportMessage", "Please report this error, either with an image of the screen or by copying the following error text (it is appreciable to provide a description of the operations performed before the error):"), + CONFIRMATION_MESSAGE_BEFORE_DELETE_BACKUP(TCategory.DIALOGS, "ConfirmationMessageBeforeDeleteBackup", "Are you sure you want to delete this item? Please note, this action cannot be undone"), + CONFIRMATION_MESSAGE_CANCEL_AUTO_BACKUP(TCategory.DIALOGS, "ConfirmationMessageCancelAutoBackup", "Are you sure you want to cancel automatic backups for this entry?"), + AUTO_BACKUP_ACTIVATED_MESSAGE(TCategory.DIALOGS, "AutoBackupActivatedMessage", "Auto Backup has been activated"), + AUTO_BACKUP_MESSAGE(TCategory.DIALOGS, "AutoBackup", "Auto Backup"), + SETTED_EVERY_MESSAGE(TCategory.DIALOGS, "SettedEveryMessage", "\nIs setted every"), + DAYS_MESSAGE(TCategory.DIALOGS, "DaysMessage", " days"), + INTERRUPT_BACKUP_PROCESS_MESSAGE(TCategory.DIALOGS, "InterruptBackupProcessMessage", "Are you sure you want to stop this backup?"), + ERROR_MESSAGE_INPUT_MISSING_GENERIC(TCategory.DIALOGS, "ErrorMessageInputMissingGeneric", "Input Missing!"), + ERROR_MESSAGE_PATH_NOT_EXISTING(TCategory.DIALOGS, "ErrorMessageForPathNotExisting", "One or both paths do not exist!"), + ERROR_MESSAGE_SAME_PATHS_GENERIC(TCategory.DIALOGS, "ErrorMessageForSamePaths", "The initial path and destination path cannot be the same. Please choose different paths!"), + ERROR_MESSAGE_COUNTING_FILES(TCategory.DIALOGS, "ErrorMessageCountingFiles", "Error occurred while calculating files to back up."), + ERROR_MESSAGE_ZIPPING_GENERIC(TCategory.DIALOGS, "ErrorMessageZippingGeneric", "Error occurred while zipping files."), + ERROR_MESSAGE_ZIPPING_IO(TCategory.DIALOGS, "ErrorMessageZippingIO", "Error occurred while zipping files: I/O error."), + ERROR_MESSAGE_ZIPPING_SECURITY(TCategory.DIALOGS, "ErrorMessageZippingSecurity", "Error occurred while zipping files: Security error."), + SUCCESS_GENERIC_TITLE(TCategory.DIALOGS, "SuccessGenericTitle", "Success"), + CSV_NAME_MESSAGE_INPUT(TCategory.DIALOGS, "CsvNameMessageInput", "Enter the name of the CSV file."), + DUPLICATED_FILE_NAME_MESSAGE(TCategory.DIALOGS, "DuplicatedFileNameMessage", "File already exists. Overwrite?"), + + // Subscription + SUBSCRIPTION_EXPIRING_TITLE(TCategory.SUBSCRIPTION, "ExpiringTitle", "Backup Manager subscription expiring soon"), + SUBSCRIPTION_EXPIRING_MESSAGE(TCategory.SUBSCRIPTION, "ExpiringMessage", "Your Backup Manager subscription is about to expire.\nAutomatic backups will continue to run until the expiration date.\nPlease contact support to renew it."), + SUBSCRIPTION_EXPIRED_TITLE(TCategory.SUBSCRIPTION, "ExpiredTitle", "Backup Manager subscription expired"), + SUBSCRIPTION_EXPIRED_MESSAGE(TCategory.SUBSCRIPTION, "ExpiredMessage", "Your Backup Manager subscription has expired.\nAutomatic backups will no longer run.\nPlease contact support to reactivate it."), + SUBSCRIPTION_ACTIVE(TCategory.SUBSCRIPTION, "ActiveLabel", "Active"), + SUBSCRIPTION_EXPIRING(TCategory.SUBSCRIPTION, "ExpiringLabel", "Expiring"), + SUBSCRIPTION_EXPIRED(TCategory.SUBSCRIPTION, "ExpiredLabel", "Expired"), + SUBSCRIPTION_STATUS(TCategory.SUBSCRIPTION, "Status", "Subscription status"), + SUBSCRIPTION_VALID_FROM(TCategory.SUBSCRIPTION, "ValidFrom", "Valid from"), + SUBSCRIPTION_VALID_TO(TCategory.SUBSCRIPTION, "ValidTo", "Valid to"), + SUBSCRIPTION_TO_EXTEND(TCategory.SUBSCRIPTION, "ToExtend", "to extend the subscription period."), + + // DASHBOARD + DASHBOARD_TITLE(TCategory.DASHBOARD, "DashboardTitle", "Backup Analytics Dashboard"), + DASHBOARD_CARD_TOTAL_CONFIGURATIONS(TCategory.DASHBOARD, "DashboardCardTotalConfigurations", "Total Backup Configurations"), + DASHBOARD_CARD_TOTAL_EXECUTIONS(TCategory.DASHBOARD, "DashboardCardTotalExecutions", "Total Backup Executions"), + DASHBOARD_CARD_SUCCESS_RATE(TCategory.DASHBOARD, "DashboardCardSuccessRate", "Success rate"), + DASHBOARD_CARD_AVG_DURATION(TCategory.DASHBOARD, "DashboardCardAvgDuration", "Avg Backup Duration"), + DASHBOARD_CARD_COMPRESSION_RATE(TCategory.DASHBOARD, "DashboardCardCompressionRate", "Compression Rate"), + DASHBOARD_CHART_EXECUTIONS(TCategory.DASHBOARD, "DashboardChartExecutions", "Backup Executions"), + DASHBOARD_CHART_AVG_DURATION(TCategory.DASHBOARD, "DashboardChartAvgDuration", "Average Backup Duration (min)"), + + // HISTORY_LOGS + HISTORY_LOGS_TITLE(TCategory.HISTORY_LOGS, "HistoryLogsTitle", "History logs"), + HISTORY_LOGS_DESCRIPTION(TCategory.HISTORY_LOGS, "HistoryLogsDescription", "Here you can find the application logs, useful for troubleshooting and understanding the application's behavior over time."), + + // ABOUT + ABOUT_SYSTEM_INFORMATION(TCategory.ABOUT, "AboutSystemInformation", "System Information"), + ABOUT_MESSAGE_BODY(TCategory.ABOUT, "AboutMessageBody", "Backup Manager is a simple and powerful application designed to automate folder and subfolder backups.

Users can schedule automatic backups or execute manual backups anytime.

Backup history is stored securely, allowing full control over saved data.

Visit project website for more information.

"), + + // SETTINGS + SETTINGS_LAYOUT_TAB(TCategory.SETTINGS, "SettingsLayoutTab", "Layout"), + SETTINGS_STYLE_TAB(TCategory.SETTINGS, "SettingsStyleTab", "Style"), + SETTINGS_WINDOWS_LAYOUT(TCategory.SETTINGS, "SettingsWindowsLayout", "Windows Layout"), + SETTINGS_WINDOWS_RIGHT(TCategory.SETTINGS, "SettingsWindowsRight", "Right to Left"), + SETTINGS_WINDOWS_FULL(TCategory.SETTINGS, "SettingsWindowsFull", "Full Window Content"), + SETTINGS_DRAWER_LAYOUT(TCategory.SETTINGS, "SettingsDrawerLayout", "Drawer layout"), + SETTINGS_DRAWER_LEFT(TCategory.SETTINGS, "SettingsDrawerLeft", "Left"), + SETTINGS_DRAWER_LEADING(TCategory.SETTINGS, "SettingsDrawerLeading", "Leading"), + SETTINGS_DRAWER_TRAILING(TCategory.SETTINGS, "SettingsDrawerTrailing", "Trailing"), + SETTINGS_DRAWER_RIGHT(TCategory.SETTINGS, "SettingsDrawerRight", "Right"), + SETTINGS_DRAWER_TOP(TCategory.SETTINGS, "SettingsDrawerTop", "Top"), + SETTINGS_DRAWER_BOTTOM(TCategory.SETTINGS, "SettingsDrawerBottom", "Bottom"), + SETTINGS_MODAL_OPTION(TCategory.SETTINGS, "SettingsModalOption", "Default modal option"), + SETTINGS_MODAL_ANIMATION(TCategory.SETTINGS, "SettingsModalAnimation", "Animation enable"), + SETTINGS_MODAL_CLOSE(TCategory.SETTINGS, "SettingsModalClose", "Close on pressed escape"), + SETTINGS_LANGUAGES_LAYOUT(TCategory.SETTINGS, "SettingsLanguagesLayout", "Language"), + SETTINGS_ACCENT_LAYOUT(TCategory.SETTINGS, "SettingsAccentLayout", "Accent color"), + SETTINGS_COLOR_PICKER_LAYOUT(TCategory.SETTINGS, "SettingsColorPickerLayout", "Color Picker"), + SETTINGS_DRAWER_LINE_LAYOUT(TCategory.SETTINGS, "SettingsDrawerLineLayout", "Drawer line style"), + SETTINGS_DRAWER_LINE_CURVED(TCategory.SETTINGS, "SettingsDrawerLineCurved", "Curved line style"), + SETTINGS_DRAWER_DOT_LINE(TCategory.SETTINGS, "SettingsDrawerDotLine", "Straight dot line style"), + SETTINGS_LINE_STYLE_LAYOUT(TCategory.SETTINGS, "SettingsLineStyleLayout", "Line style option"), + SETTINGS_LINE_STYLE_RETTANGLE(TCategory.SETTINGS, "SettingsLineStyleRettangle", "Rettangle"), + SETTINGS_LINE_STYLE_ELLIPSE(TCategory.SETTINGS, "SettingsLineStyleEllipse", "Ellipse"), + SETTINGS_LINE_STYLE_LINE(TCategory.SETTINGS, "SettingsLineStyleLine", "Line"), + SETTINGS_LINE_STYLE_CURVED(TCategory.SETTINGS, "SettingsLineStyleCurved", "Curved"), + SETTINGS_COLOR_OPTION_LAYOUT(TCategory.SETTINGS, "SettingsColorOptionLayout", "Color option"), + SETTINGS_COLOR_OPTION_PAINTED(TCategory.SETTINGS, "SettingsColorOptionPainted", "Paint selected line color"), + SETTINGS_THEMES(TCategory.SETTINGS, "SettingsThemes", "Themes"), + SETTINGS_THEME_ALL(TCategory.SETTINGS, "SettingsThemeAll", "All"), + SETTINGS_THEME_LIGHT(TCategory.SETTINGS, "SettingsThemeLight", "Light"), + SETTINGS_THEME_DARK(TCategory.SETTINGS, "SettingsThemeDark", "Dark"), + + // SEARCH BAR + SEARCH_TITLE(TCategory.SEARCH_BAR, "SearcTitle", "Search..."), + SEARCH_NO_RECENT(TCategory.SEARCH_BAR, "SearchNoRecent", "No recent searches"), + SEARCH_NO_RESULT(TCategory.SEARCH_BAR, "SearchNoResult", "No result for"), + SEARCH_FAVORITE(TCategory.SEARCH_BAR, "SearchFavorite", "Favorite"), + SEARCH_RECENT(TCategory.SEARCH_BAR, "SearchRecent", "Recents"), + + // TOAST MESSAGES + TOAST_BACKUP_EDITED(TCategory.TOAST, "BackupEditedOk", "Backup updated successfully"), + TOAST_BACKUP_CREATED(TCategory.TOAST, "BackupCreatedOk", "Backup created successfully"), + TOAST_BACKUP_DELETED(TCategory.TOAST, "BackupDeletedOk", "Backup deleted successfully"), + TOAST_BACKUP_REPLACED(TCategory.TOAST, "BackupReplacedOk", "Existing backup replaced successfully"), + TOAST_BACKUP_DELETED_ERROR(TCategory.TOAST, "BackupDeletedError", "Failed to delete the backup"), + TOAST_BACKUP_REPLACED_ERROR(TCategory.TOAST, "BackupReplacedError", "Failed to replace the backup"), + TOAST_INVALID_TIME(TCategory.TOAST, "InvalidTime", "Invalid time interval"), + TOAST_SUBSCRIPTION_EXPIRING(TCategory.TOAST, "SubscriptionExpiring", "Your Backup Manager subscription will expire soon. Please contact us to renew it"), + TOAST_SUBSCRIPTION_EXPIRED(TCategory.TOAST, "SubscriptionExpired", "Your Backup Manager subscription has expired. Please contact us to renew it"), + TOAST_LANGUAGE_CHANGE(TCategory.TOAST, "LanguageChange", "Some changes will take effect after restarting the application"), + TOAST_MISSING_DATA_LOGIN_ERROR(TCategory.TOAST, "MissingDataLoginError", "Please fill in all the required fields"), + TOAST_WRONG_EMAIL_LOGIN_ERROR(TCategory.TOAST, "WrongEmailLoginError", "The provided email address is invalid. Please provide a correct one"), + TOAST_LOGIN(TCategory.TOAST, "LoginOk", "Welcome! You have successfully signed in"), + TOAST_ERROR_TEXT_CLIPBOARD(TCategory.TOAST, "ErrorTextClipboard", "Error text has been copied to the clipboard"), + TOAST_CSV_EXPORT(TCategory.TOAST, "CsvExport", "Backups exported to CSV successfully!"), + TOAST_CSV_EXPORT_ERROR(TCategory.TOAST, "CsvExportError", "Error exporting backups to CSV"), + TOAST_CSV_EXPORT_INVALID_FILENAME(TCategory.TOAST, "CsvExportInvalidFilename", "Invalid file name. Use only alphanumeric characters, dashes, and underscores"), + TOAST_NOT_SUPPORTED_EMAIL(TCategory.TOAST, "NotSupportedEmail", "Your system does not support sending emails directly from this application"), + TOAST_UNABLE_TO_SEND_EMAIL(TCategory.TOAST, "UnableToSendEmail", "Unable to send email. Please try again later"), + TOAST_NOT_SUPPORTED_EMAIL_GENERIC(TCategory.TOAST, "NotSupportedEmailGeneric", "Your system does not support sending emails"), + TOAST_OPENING_WEBSITE_ERROR(TCategory.TOAST, "OpeningWebsiteError", "Failed to open the web page. Please try again"), + TOAST_FOLDER_NOT_EXISTING(TCategory.TOAST, "FolderNotExisting", "The folder does not exist or is invalid"), + TOAST_BACKUP_NAME_ALREADY_USED(TCategory.TOAST, "BackupNameAlreadyUsed", "Backup name already used!"), + TOAST_BACKUP_ALREADY_IN_PROGRESS(TCategory.TOAST, "BackupAlreadyInProgress", "There is already a backup in progress. It is not possible to perform parallel backups"), + TOAST_BACKUP_PATHS_EMPTY_ERROR(TCategory.TOAST, "BackupPathsEmptyError", "The initial path and destination path cannot be empty!"), + ; + + private final TCategory category; + private final String keyName; + private final String defaultValue; + + private static final Map lookup = new HashMap<>(); + + static { + for (TKey key : TKey.values()) { + lookup.put(key.keyName, key); + } + } + + // Constructor to assign both key and default value + private TKey(TCategory category, String keyName, String defaultValue) { + this.category = category; + this.keyName = keyName; + this.defaultValue = defaultValue; + } + + // Lookup by keyName (JSON key) + public static TKey fromKeyName(String keyName) { + return lookup.get(keyName); + } + + public TCategory getCategory() { return category; } + public String getKeyName() { return keyName; } + public String getDefaultValue() { return defaultValue; } + } + + public static String get(TKey key) { + TCategory category = key.getCategory(); + return category.getTranslation(key); + } + + public static void loadTranslations(String filePath) throws IOException { + Gson gson = new Gson(); + + // Clear previous translations to avoid stale values when switching languages + for (TCategory c : TCategory.values()) { + c.clearTranslations(); + } + + try (FileReader reader = new FileReader(filePath, StandardCharsets.UTF_8)) { + JsonObject jsonObject = gson.fromJson(reader, JsonObject.class); + + for (TCategory category : TCategory.values()) { + JsonObject categoryTranslations = jsonObject.getAsJsonObject(category.getCategoryName()); + + if (categoryTranslations == null) { + logger.warn("Missing category in {}: {}", LanguageManager.getLanguage().getFileName(), category.getCategoryName()); + continue; + } + + Set loadedKeys = new HashSet<>(); + for (Map.Entry entry : categoryTranslations.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue().getAsString(); + + // Use fromKeyName to get the TKey from the JSON key + TKey translationKey = TKey.fromKeyName(key); + if (translationKey != null) { + // If value is null or empty, fall back to the default value from the enum + String translationValue = (value != null && !value.isEmpty()) ? value : translationKey.getDefaultValue(); + category.addTranslation(translationKey, translationValue); + loadedKeys.add(translationKey); + } else { + logger.warn("Unrecognized key in JSON: {}, using default value", key); + } + } + + for (TKey key : TKey.values()) { + if (key.getCategory() == category && !loadedKeys.contains(key)) { + logger.warn("Missing translation in {} -> category: {}, key: {}", LanguageManager.getLanguage().getFileName(), key.getCategory(), key.getKeyName()); + } + } + } + } catch (Exception ex) { + logger.error("An error occurred: {}", ex.getMessage(), ex); + } + } +} diff --git a/src/main/java/backupmanager/Exceptions/BackupDeletionException.java b/src/main/java/backupmanager/Exceptions/BackupDeletionException.java new file mode 100644 index 00000000..7e9b02b5 --- /dev/null +++ b/src/main/java/backupmanager/Exceptions/BackupDeletionException.java @@ -0,0 +1,19 @@ +package backupmanager.Exceptions; + +public class BackupDeletionException extends Exception { + public BackupDeletionException() { + super(); + } + + public BackupDeletionException(String message) { + super(message); + } + + public BackupDeletionException(String message, Throwable cause) { + super(message, cause); + } + + public BackupDeletionException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/backupmanager/GUI/BackupManagerGUI.form b/src/main/java/backupmanager/GUI/BackupManagerGUI.form deleted file mode 100644 index 45880cec..00000000 --- a/src/main/java/backupmanager/GUI/BackupManagerGUI.form +++ /dev/null @@ -1,899 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - <Editor/> - <Renderer/> - </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> - <Title/> - <Editor/> - <Renderer/> - </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> - <Title/> - <Editor/> - <Renderer/> - </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> - <Title/> - <Editor/> - <Renderer/> - </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> - <Title/> - <Editor/> - <Renderer/> - </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> - <Title/> - <Editor/> - <Renderer/> - </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> - <Title/> - <Editor/> - <Renderer/> - </Column> - </TableColumnModel> - </Property> - <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor"> - <Color id="Cursore predefinito"/> - </Property> - <Property name="rowHeight" type="int" value="50"/> - <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> - <TableHeader reorderingAllowed="true" resizingAllowed="true"/> - </Property> - </Properties> - <Events> - <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="tableMouseClicked"/> - </Events> - </Component> - </SubComponents> - </Container> - <Component class="backupmanager.svg.SVGButton" name="addBackupEntryButton"> - <Properties> - <Property name="toolTipText" type="java.lang.String" value="Add new backup"/> - <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[32, 32]"/> - </Property> - <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[32, 32]"/> - </Property> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addBackupEntryButtonActionPerformed"/> - </Events> - </Component> - <Component class="backupmanager.svg.SVGButton" name="exportAsCsvBtn"> - <Properties> - <Property name="toolTipText" type="java.lang.String" value="Export as .csv"/> - <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[32, 32]"/> - </Property> - <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[32, 32]"/> - </Property> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exportAsCsvBtnActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JLabel" name="ExportLabel"> - <Properties> - <Property name="horizontalAlignment" type="int" value="4"/> - <Property name="text" type="java.lang.String" value="Export As:"/> - </Properties> - </Component> - </SubComponents> - </Container> - <Container class="javax.swing.JPanel" name="panelDashboard"> - <Constraints> - <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription"> - <CardConstraints cardName="Dashboard"/> - </Constraint> - </Constraints> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="1" attributes="0"> - <EmptySpace min="-2" pref="52" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <Component id="totalBackupsPanel" min="-2" max="-2" attributes="0"/> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="totalBackupConfigurationsPanel" min="-2" max="-2" attributes="0"/> - </Group> - <Component id="totalSpaceUsedPanel" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace pref="228" max="32767" attributes="0"/> - <Group type="103" groupAlignment="1" max="-2" attributes="0"> - <Component id="chart2" alignment="1" pref="300" max="32767" attributes="0"/> - <Component id="chart1" alignment="1" pref="300" max="32767" attributes="0"/> - <Component id="chart2TitleLabel" alignment="1" pref="300" max="32767" attributes="0"/> - <Component id="chart1TitleLabel" alignment="1" max="32767" attributes="0"/> - </Group> - <EmptySpace min="-2" pref="16" max="-2" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="1" attributes="0"> - <EmptySpace min="-2" pref="18" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <Component id="chart1TitleLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="chart1" min="-2" max="-2" attributes="0"/> - </Group> - <Component id="totalBackupConfigurationsPanel" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="chart2TitleLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="chart2" min="-2" max="-2" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <Component id="totalBackupsPanel" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" pref="20" max="-2" attributes="0"/> - <Component id="totalSpaceUsedPanel" min="-2" max="-2" attributes="0"/> - </Group> - </Group> - <EmptySpace pref="34" max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Container class="javax.swing.JPanel" name="chart2"> - <Properties> - <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> - <Border info="org.netbeans.modules.form.compat2.border.SoftBevelBorderInfo"> - <BevelBorder/> - </Border> - </Property> - <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[230, 180]"/> - </Property> - </Properties> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="0" max="32767" attributes="0"/> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="174" max="32767" attributes="0"/> - </Group> - </DimensionLayout> - </Layout> - </Container> - <Container class="javax.swing.JPanel" name="chart1"> - <Properties> - <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> - <Border info="org.netbeans.modules.form.compat2.border.SoftBevelBorderInfo"> - <BevelBorder/> - </Border> - </Property> - <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[230, 180]"/> - </Property> - </Properties> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="0" max="32767" attributes="0"/> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="174" max="32767" attributes="0"/> - </Group> - </DimensionLayout> - </Layout> - </Container> - <Component class="javax.swing.JLabel" name="chart1TitleLabel"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="0"/> - </Property> - <Property name="horizontalAlignment" type="int" value="0"/> - <Property name="text" type="java.lang.String" value="Title Chart 1"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="chart2TitleLabel"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="0"/> - </Property> - <Property name="horizontalAlignment" type="int" value="0"/> - <Property name="text" type="java.lang.String" value="Title Chart 2"/> - </Properties> - </Component> - <Container class="javax.swing.JPanel" name="totalBackupsPanel"> - <Properties> - <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> - <Border info="org.netbeans.modules.form.compat2.border.MatteColorBorderInfo"> - <MatteColorBorder/> - </Border> - </Property> - </Properties> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="totalBackupsNumber" max="32767" attributes="0"/> - <Component id="totalBackupsTitleLabel" pref="194" max="32767" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="totalBackupsTitleLabel" min="-2" pref="40" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="totalBackupsNumber" min="-2" pref="25" max="-2" attributes="0"/> - <EmptySpace min="0" pref="31" max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Component class="javax.swing.JLabel" name="totalBackupsTitleLabel"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="0"/> - </Property> - <Property name="text" type="java.lang.String" value="Total Backups"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="totalBackupsNumber"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="1"/> - </Property> - <Property name="text" type="java.lang.String" value="10"/> - </Properties> - </Component> - </SubComponents> - </Container> - <Container class="javax.swing.JPanel" name="totalBackupConfigurationsPanel"> - <Properties> - <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> - <Border info="org.netbeans.modules.form.compat2.border.MatteColorBorderInfo"> - <MatteColorBorder/> - </Border> - </Property> - <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[214, 104]"/> - </Property> - </Properties> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="totalBackupConfigurationsNumber" alignment="0" max="32767" attributes="0"/> - <Component id="totalBackupConfigurationsTitleLabel" alignment="0" pref="194" max="32767" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="totalBackupConfigurationsTitleLabel" min="-2" pref="40" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="totalBackupConfigurationsNumber" min="-2" pref="25" max="-2" attributes="0"/> - <EmptySpace min="0" pref="31" max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Component class="javax.swing.JLabel" name="totalBackupConfigurationsTitleLabel"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="0"/> - </Property> - <Property name="text" type="java.lang.String" value="Total Backups"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="totalBackupConfigurationsNumber"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="1"/> - </Property> - <Property name="text" type="java.lang.String" value="10"/> - </Properties> - </Component> - </SubComponents> - </Container> - <Container class="javax.swing.JPanel" name="totalSpaceUsedPanel"> - <Properties> - <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> - <Border info="org.netbeans.modules.form.compat2.border.MatteColorBorderInfo"> - <MatteColorBorder/> - </Border> - </Property> - <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[214, 104]"/> - </Property> - </Properties> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="totalSpaceNumber" alignment="0" max="32767" attributes="0"/> - <Component id="totalSpaceTitleLabel" alignment="0" pref="194" max="32767" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="totalSpaceTitleLabel" min="-2" pref="40" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="totalSpaceNumber" min="-2" pref="25" max="-2" attributes="0"/> - <EmptySpace min="0" pref="31" max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Component class="javax.swing.JLabel" name="totalSpaceTitleLabel"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="0"/> - </Property> - <Property name="text" type="java.lang.String" value="Total Space Used"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="totalSpaceNumber"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Segoe UI" size="18" style="1"/> - </Property> - <Property name="text" type="java.lang.String" value="10"/> - </Properties> - </Component> - </SubComponents> - </Container> - </SubComponents> - </Container> - </SubComponents> - </Container> - </SubComponents> -</Form> diff --git a/src/main/java/backupmanager/GUI/BackupManagerGUI.java b/src/main/java/backupmanager/GUI/BackupManagerGUI.java deleted file mode 100644 index a2cdc721..00000000 --- a/src/main/java/backupmanager/GUI/BackupManagerGUI.java +++ /dev/null @@ -1,1212 +0,0 @@ -package backupmanager.GUI; - -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.swing.AbstractAction; -import javax.swing.ActionMap; -import javax.swing.InputMap; -import javax.swing.JComponent; -import javax.swing.JOptionPane; -import javax.swing.JScrollPane; -import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableColumnModel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.formdev.flatlaf.FlatClientProperties; - -import backupmanager.Controllers.GuiController; -import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Entities.Confingurations; -import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.MenuItems; -import backupmanager.Enums.TranslationLoaderEnum; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.Controllers.BackupManagerController; -import backupmanager.GUI.Controllers.BackupMenuController; -import backupmanager.GUI.Controllers.BackupPopupController; -import backupmanager.Helpers.BackupHelper; -import static backupmanager.Helpers.BackupHelper.dateForfolderNameFormatter; -import static backupmanager.Helpers.BackupHelper.formatter; -import backupmanager.Json.JSONConfigReader; -import backupmanager.Managers.ExceptionManager; -import backupmanager.Managers.ExportManager; -import backupmanager.Managers.ThemeManager; -import backupmanager.Services.BackupObserver; -import backupmanager.Services.BackupService; -import backupmanager.Table.BackupTable; -import backupmanager.Table.BackupTableModel; -import backupmanager.Table.CheckboxCellRenderer; -import backupmanager.Table.StripedRowRenderer; -import backupmanager.Widgets.SideMenuPanel; -import backupmanager.database.Repositories.BackupConfigurationRepository; - -public final class BackupManagerGUI extends javax.swing.JFrame { - private static final Logger logger = LoggerFactory.getLogger(BackupManagerGUI.class); - - private final BackupManagerController backupManagerController; - private final BackupObserver observer; - public static List<ConfigurationBackup> backups; - public static DefaultTableModel model; - public static BackupTable backupTable; - public static BackupTableModel tableModel; - public static BackupProgressGUI progressBar; - private Integer selectedRow; - private final SideMenuPanel sp; - - public BackupManagerGUI() { - ThemeManager.updateThemeFrame(this); - - initComponents(); - - backupManagerController = new BackupManagerController(new BackupService()); - - sp = new SideMenuPanel(this); - - this.setIconImage(GuiController.getIcon(this.getClass())); - - initializeMenuItems(); - setScreenSize(); - researchField.putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_ICON, new com.formdev.flatlaf.extras.FlatSVGIcon("res/img/search.svg", 16, 16)); - setTranslations(); - - // first initialize the table, then start observer thread - initializeTable(); - observer = new BackupObserver(dateForfolderNameFormatter, 1000); - observer.start(); - - interruptBackupPopupItem.setEnabled(false); - - setSvgImages(); - backupManagerController.checkForFirstAccess(this); - jSeparator5.setVisible(false); - - // TODO: remove this - interruptBackupPopupItem.setVisible(false); - - initSidebar(); - } - - private void initSidebar() { - sp.setMain(null); - sp.setMinWidth(55); - sp.setMaxWidth(150); - sp.setMainAnimationEnabled(true); - sp.setSpeed(4); - sp.setResponsiveMinWidth(1300); - } - - public void showWindow() { - setVisible(true); - toFront(); - requestFocus(); - } - - private void setScreenSize() { - int[] screenSize = backupManagerController.getScreenSize(); - this.setSize(screenSize[0], screenSize[1]); - } - - public void reloadPreferences() { - logger.info("Reloading preferences"); - - Confingurations.updateAllConfigurations(); - - // load language - try { - TranslationLoaderEnum.loadTranslations(ConfigKey.LANGUAGES_DIRECTORY_STRING.getValue() + Confingurations.getLanguage().getFileName()); - setTranslations(); - } catch (IOException ex) { - logger.error("An error occurred during reloading preferences operation: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - - // load theme - ThemeManager.updateThemeFrame(this); - ThemeManager.refreshPopup(TablePopup); - setSvgImages(); - } - - private void displayBackupList() { - BackupTableModel tempModel = new BackupTableModel(getColumnTranslations(), 0); - BackupManagerGUI main = this; - - // Populate the model with backup data - for (ConfigurationBackup backup : backups) { - tempModel.addRow(new Object[]{ - backup.getName(), - backup.getTargetPath(), - backup.getDestinationPath(), - backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "", - backup.isAutomatic(), - backup.getNextBackupDate() != null ? backup.getNextBackupDate().format(formatter) : "", - backup.getTimeIntervalBackup() != null ? backup.getTimeIntervalBackup().toString() : "" - }); - } - - backupTable = new BackupTable(tempModel); - - // Add key bindings using InputMap and ActionMap - InputMap inputMap = backupTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - ActionMap actionMap = backupTable.getActionMap(); - - // Handle Enter key - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enterKey"); - actionMap.put("enterKey", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - backupManagerController.handleEnterKeyPressOnTable(main, backupTable); - } - }); - - // Handle Delete key - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "deleteKey"); - actionMap.put("deleteKey", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - backupManagerController.handleDeleteKeyPressOnTable(backupTable); - } - }); - - // Apply renderers for each column - TableColumnModel columnModel = backupTable.getColumnModel(); - - for (int i = 0; i < columnModel.getColumnCount(); i++) { - if (i == 4) { - columnModel.getColumn(i).setCellRenderer(new CheckboxCellRenderer()); - columnModel.getColumn(i).setCellEditor(backupTable.getDefaultEditor(Boolean.class)); - } else { - columnModel.getColumn(i).setCellRenderer(new StripedRowRenderer()); - } - } - - // Add the existing mouse listener to the new table - backupTable.addMouseListener(new java.awt.event.MouseAdapter() { - @Override - public void mouseClicked(java.awt.event.MouseEvent evt) { - tableMouseClicked(evt); // Reuse the existing method - } - }); - - // Update the global model reference - BackupManagerGUI.model = tempModel; - - // Replace the existing table in the GUI - JScrollPane scrollPane = (JScrollPane) table.getParent().getParent(); - table = backupTable; // Update the reference to the new table - scrollPane.setViewportView(table); // Replace the table in the scroll pane - } - - /** - * 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. - */ - @SuppressWarnings("unchecked") - // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents - private void initComponents() { - - TablePopup = new javax.swing.JPopupMenu(); - EditPoputItem = new javax.swing.JMenuItem(); - DeletePopupItem = new javax.swing.JMenuItem(); - interruptBackupPopupItem = new javax.swing.JMenuItem(); - DuplicatePopupItem = new javax.swing.JMenuItem(); - renamePopupItem = new javax.swing.JMenuItem(); - jSeparator1 = new javax.swing.JPopupMenu.Separator(); - OpenInitialFolderItem = new javax.swing.JMenuItem(); - OpenInitialDestinationItem = new javax.swing.JMenuItem(); - jSeparator3 = new javax.swing.JPopupMenu.Separator(); - Backup = new javax.swing.JMenu(); - RunBackupPopupItem = new javax.swing.JMenuItem(); - AutoBackupMenuItem = new javax.swing.JCheckBoxMenuItem(); - jSeparator2 = new javax.swing.JPopupMenu.Separator(); - jMenu4 = new javax.swing.JMenu(); - CopyBackupNamePopupItem = new javax.swing.JMenuItem(); - CopyInitialPathPopupItem = new javax.swing.JMenuItem(); - CopyDestinationPathPopupItem = new javax.swing.JMenuItem(); - panelVersion = new javax.swing.JPanel(); - jLabel3 = new javax.swing.JLabel(); - layeredCardPanel = new javax.swing.JLayeredPane(); - panelBackupList = new javax.swing.JPanel(); - detailsLabel = new javax.swing.JLabel(); - researchField = new javax.swing.JTextField(); - exportAsPdfBtn = new backupmanager.svg.SVGButton(); - jLabel1 = new javax.swing.JLabel(); - jScrollPane1 = new javax.swing.JScrollPane(); - table = new javax.swing.JTable(); - addBackupEntryButton = new backupmanager.svg.SVGButton(); - exportAsCsvBtn = new backupmanager.svg.SVGButton(); - ExportLabel = new javax.swing.JLabel(); - panelDashboard = new javax.swing.JPanel(); - chart2 = new javax.swing.JPanel(); - chart1 = new javax.swing.JPanel(); - chart1TitleLabel = new javax.swing.JLabel(); - chart2TitleLabel = new javax.swing.JLabel(); - totalBackupsPanel = new javax.swing.JPanel(); - totalBackupsTitleLabel = new javax.swing.JLabel(); - totalBackupsNumber = new javax.swing.JLabel(); - totalBackupConfigurationsPanel = new javax.swing.JPanel(); - totalBackupConfigurationsTitleLabel = new javax.swing.JLabel(); - totalBackupConfigurationsNumber = new javax.swing.JLabel(); - totalSpaceUsedPanel = new javax.swing.JPanel(); - totalSpaceTitleLabel = new javax.swing.JLabel(); - totalSpaceNumber = new javax.swing.JLabel(); - jMenuBar1 = new javax.swing.JMenuBar(); - jMenu1 = new javax.swing.JMenu(); - MenuNew = new backupmanager.svg.SVGMenuItem(); - MenuSave = new backupmanager.svg.SVGMenuItem(); - MenuSaveWithName = new backupmanager.svg.SVGMenuItem(); - jSeparator4 = new javax.swing.JPopupMenu.Separator(); - MenuImport = new backupmanager.svg.SVGMenuItem(); - MenuExport = new backupmanager.svg.SVGMenuItem(); - jSeparator5 = new javax.swing.JPopupMenu.Separator(); - MenuClear = new backupmanager.svg.SVGMenuItem(); - MenuHistory = new backupmanager.svg.SVGMenuItem(); - jMenu2 = new javax.swing.JMenu(); - MenuPreferences = new backupmanager.svg.SVGMenuItem(); - MenuQuit = new backupmanager.svg.SVGMenuItem(); - jMenu3 = new javax.swing.JMenu(); - MenuWebsite = new backupmanager.svg.SVGMenuItem(); - MenuInfoPage = new backupmanager.svg.SVGMenuItem(); - MenuShare = new backupmanager.svg.SVGMenuItem(); - MenuDonate = new backupmanager.svg.SVGMenu(); - MenuPaypalDonate = new backupmanager.svg.SVGMenuItem(); - MenuBuyMeACoffeDonate = new backupmanager.svg.SVGMenuItem(); - jMenu5 = new javax.swing.JMenu(); - MenuBugReport = new backupmanager.svg.SVGMenuItem(); - MenuSupport = new backupmanager.svg.SVGMenuItem(); - - EditPoputItem.setText("Edit"); - EditPoputItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - EditPoputItemActionPerformed(evt); - } - }); - TablePopup.add(EditPoputItem); - - DeletePopupItem.setText("Delete"); - DeletePopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - DeletePopupItemActionPerformed(evt); - } - }); - TablePopup.add(DeletePopupItem); - - interruptBackupPopupItem.setText("Interrupt"); - interruptBackupPopupItem.setToolTipText(""); - interruptBackupPopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - interruptBackupPopupItemActionPerformed(evt); - } - }); - TablePopup.add(interruptBackupPopupItem); - - DuplicatePopupItem.setText("Duplicate"); - DuplicatePopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - DuplicatePopupItemActionPerformed(evt); - } - }); - TablePopup.add(DuplicatePopupItem); - - renamePopupItem.setText("Rename backup"); - renamePopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - renamePopupItemActionPerformed(evt); - } - }); - TablePopup.add(renamePopupItem); - TablePopup.add(jSeparator1); - - OpenInitialFolderItem.setText("Open initial folder"); - OpenInitialFolderItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - OpenInitialFolderItemActionPerformed(evt); - } - }); - TablePopup.add(OpenInitialFolderItem); - - OpenInitialDestinationItem.setText("Open destination folder"); - OpenInitialDestinationItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - OpenInitialDestinationItemActionPerformed(evt); - } - }); - TablePopup.add(OpenInitialDestinationItem); - TablePopup.add(jSeparator3); - - Backup.setText("Backup"); - - RunBackupPopupItem.setText("Run single backup"); - RunBackupPopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - RunBackupPopupItemActionPerformed(evt); - } - }); - Backup.add(RunBackupPopupItem); - - AutoBackupMenuItem.setSelected(true); - AutoBackupMenuItem.setText("Auto Backup"); - AutoBackupMenuItem.setToolTipText(""); - AutoBackupMenuItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - AutoBackupMenuItemActionPerformed(evt); - } - }); - Backup.add(AutoBackupMenuItem); - - TablePopup.add(Backup); - TablePopup.add(jSeparator2); - - jMenu4.setText("Copy text"); - - CopyBackupNamePopupItem.setText("Copy backup name"); - CopyBackupNamePopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - CopyBackupNamePopupItemActionPerformed(evt); - } - }); - jMenu4.add(CopyBackupNamePopupItem); - - CopyInitialPathPopupItem.setText("Copy initial path"); - CopyInitialPathPopupItem.setToolTipText(""); - CopyInitialPathPopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - CopyInitialPathPopupItemActionPerformed(evt); - } - }); - jMenu4.add(CopyInitialPathPopupItem); - - CopyDestinationPathPopupItem.setText("Copy destination path"); - CopyDestinationPathPopupItem.setToolTipText(""); - CopyDestinationPathPopupItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - CopyDestinationPathPopupItemActionPerformed(evt); - } - }); - jMenu4.add(CopyDestinationPathPopupItem); - - TablePopup.add(jMenu4); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle("Backup Manager"); - setMinimumSize(new java.awt.Dimension(750, 450)); - - panelVersion.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); - panelVersion.setPreferredSize(new java.awt.Dimension(100, 30)); - - jLabel3.setText("Version 2.0.2"); - jLabel3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - - javax.swing.GroupLayout panelVersionLayout = new javax.swing.GroupLayout(panelVersion); - panelVersion.setLayout(panelVersionLayout); - panelVersionLayout.setHorizontalGroup( - panelVersionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelVersionLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 997, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - panelVersionLayout.setVerticalGroup( - panelVersionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelVersionLayout.createSequentialGroup() - .addComponent(jLabel3) - .addGap(0, 4, Short.MAX_VALUE)) - ); - - getContentPane().add(panelVersion, java.awt.BorderLayout.PAGE_END); - - layeredCardPanel.setLayout(new java.awt.CardLayout()); - - panelBackupList.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); - panelBackupList.setVerifyInputWhenFocusTarget(false); - - detailsLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP); - - researchField.setToolTipText("Research bar"); - researchField.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyReleased(java.awt.event.KeyEvent evt) { - researchFieldKeyReleased(evt); - } - }); - - exportAsPdfBtn.setToolTipText("Export as .pdf"); - exportAsPdfBtn.setMaximumSize(new java.awt.Dimension(32, 32)); - exportAsPdfBtn.setMinimumSize(new java.awt.Dimension(32, 32)); - exportAsPdfBtn.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - exportAsPdfBtnActionPerformed(evt); - } - }); - - jLabel1.setFont(new java.awt.Font("Segoe UI", 0, 20)); // NOI18N - jLabel1.setText("|"); - jLabel1.setAlignmentY(0.0F); - - table.setModel(new javax.swing.table.DefaultTableModel( - new Object [][] { - - }, - new String [] { - "Backup name", "Initial path", "Destination path", "Last backup", "Auto backup", "Next date backup", "Days interval backup" - } - ) { - Class[] types = new Class [] { - java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.Boolean.class, java.lang.String.class, java.lang.Integer.class - }; - boolean[] canEdit = new boolean [] { - false, false, false, false, false, false, false - }; - - public Class getColumnClass(int columnIndex) { - return types [columnIndex]; - } - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return canEdit [columnIndex]; - } - }); - table.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - table.setRowHeight(50); - table.addMouseListener(new java.awt.event.MouseAdapter() { - public void mouseClicked(java.awt.event.MouseEvent evt) { - tableMouseClicked(evt); - } - }); - jScrollPane1.setViewportView(table); - - addBackupEntryButton.setToolTipText("Add new backup"); - addBackupEntryButton.setMaximumSize(new java.awt.Dimension(32, 32)); - addBackupEntryButton.setMinimumSize(new java.awt.Dimension(32, 32)); - addBackupEntryButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - addBackupEntryButtonActionPerformed(evt); - } - }); - - exportAsCsvBtn.setToolTipText("Export as .csv"); - exportAsCsvBtn.setMaximumSize(new java.awt.Dimension(32, 32)); - exportAsCsvBtn.setMinimumSize(new java.awt.Dimension(32, 32)); - exportAsCsvBtn.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - exportAsCsvBtnActionPerformed(evt); - } - }); - - ExportLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - ExportLabel.setText("Export As:"); - - javax.swing.GroupLayout panelBackupListLayout = new javax.swing.GroupLayout(panelBackupList); - panelBackupList.setLayout(panelBackupListLayout); - panelBackupListLayout.setHorizontalGroup( - panelBackupListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(detailsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jScrollPane1) - .addGroup(panelBackupListLayout.createSequentialGroup() - .addComponent(addBackupEntryButton, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 9, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(researchField, javax.swing.GroupLayout.PREFERRED_SIZE, 321, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(ExportLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 554, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(exportAsCsvBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(exportAsPdfBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(12, 12, 12)) - ); - panelBackupListLayout.setVerticalGroup( - panelBackupListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelBackupListLayout.createSequentialGroup() - .addGroup(panelBackupListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(ExportLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(panelBackupListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelBackupListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel1) - .addComponent(researchField, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(addBackupEntryButton, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(exportAsCsvBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(exportAsPdfBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(10, 10, 10) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE) - .addGap(12, 12, 12) - .addComponent(detailsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 96, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0)) - ); - - researchField.getAccessibleContext().setAccessibleName(""); - - layeredCardPanel.add(panelBackupList, "BackupList"); - - chart2.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); - chart2.setPreferredSize(new java.awt.Dimension(230, 180)); - - javax.swing.GroupLayout chart2Layout = new javax.swing.GroupLayout(chart2); - chart2.setLayout(chart2Layout); - chart2Layout.setHorizontalGroup( - chart2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - chart2Layout.setVerticalGroup( - chart2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 174, Short.MAX_VALUE) - ); - - chart1.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); - chart1.setPreferredSize(new java.awt.Dimension(230, 180)); - - javax.swing.GroupLayout chart1Layout = new javax.swing.GroupLayout(chart1); - chart1.setLayout(chart1Layout); - chart1Layout.setHorizontalGroup( - chart1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - chart1Layout.setVerticalGroup( - chart1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 174, Short.MAX_VALUE) - ); - - chart1TitleLabel.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N - chart1TitleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - chart1TitleLabel.setText("Title Chart 1"); - - chart2TitleLabel.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N - chart2TitleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - chart2TitleLabel.setText("Title Chart 2"); - - totalBackupsPanel.setBorder(new javax.swing.border.MatteBorder(null)); - - totalBackupsTitleLabel.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N - totalBackupsTitleLabel.setText("Total Backups"); - - totalBackupsNumber.setFont(new java.awt.Font("Segoe UI", 1, 18)); // NOI18N - totalBackupsNumber.setText("10"); - - javax.swing.GroupLayout totalBackupsPanelLayout = new javax.swing.GroupLayout(totalBackupsPanel); - totalBackupsPanel.setLayout(totalBackupsPanelLayout); - totalBackupsPanelLayout.setHorizontalGroup( - totalBackupsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(totalBackupsPanelLayout.createSequentialGroup() - .addGap(12, 12, 12) - .addGroup(totalBackupsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(totalBackupsNumber, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(totalBackupsTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE)) - .addContainerGap()) - ); - totalBackupsPanelLayout.setVerticalGroup( - totalBackupsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(totalBackupsPanelLayout.createSequentialGroup() - .addComponent(totalBackupsTitleLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(totalBackupsNumber, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 31, Short.MAX_VALUE)) - ); - - totalBackupConfigurationsPanel.setBorder(new javax.swing.border.MatteBorder(null)); - totalBackupConfigurationsPanel.setPreferredSize(new java.awt.Dimension(214, 104)); - - totalBackupConfigurationsTitleLabel.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N - totalBackupConfigurationsTitleLabel.setText("Total Backups"); - - totalBackupConfigurationsNumber.setFont(new java.awt.Font("Segoe UI", 1, 18)); // NOI18N - totalBackupConfigurationsNumber.setText("10"); - - javax.swing.GroupLayout totalBackupConfigurationsPanelLayout = new javax.swing.GroupLayout(totalBackupConfigurationsPanel); - totalBackupConfigurationsPanel.setLayout(totalBackupConfigurationsPanelLayout); - totalBackupConfigurationsPanelLayout.setHorizontalGroup( - totalBackupConfigurationsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(totalBackupConfigurationsPanelLayout.createSequentialGroup() - .addGap(12, 12, 12) - .addGroup(totalBackupConfigurationsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(totalBackupConfigurationsNumber, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(totalBackupConfigurationsTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE)) - .addContainerGap()) - ); - totalBackupConfigurationsPanelLayout.setVerticalGroup( - totalBackupConfigurationsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(totalBackupConfigurationsPanelLayout.createSequentialGroup() - .addComponent(totalBackupConfigurationsTitleLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(totalBackupConfigurationsNumber, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 31, Short.MAX_VALUE)) - ); - - totalSpaceUsedPanel.setBorder(new javax.swing.border.MatteBorder(null)); - totalSpaceUsedPanel.setPreferredSize(new java.awt.Dimension(214, 104)); - - totalSpaceTitleLabel.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N - totalSpaceTitleLabel.setText("Total Space Used"); - - totalSpaceNumber.setFont(new java.awt.Font("Segoe UI", 1, 18)); // NOI18N - totalSpaceNumber.setText("10"); - - javax.swing.GroupLayout totalSpaceUsedPanelLayout = new javax.swing.GroupLayout(totalSpaceUsedPanel); - totalSpaceUsedPanel.setLayout(totalSpaceUsedPanelLayout); - totalSpaceUsedPanelLayout.setHorizontalGroup( - totalSpaceUsedPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(totalSpaceUsedPanelLayout.createSequentialGroup() - .addGap(12, 12, 12) - .addGroup(totalSpaceUsedPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(totalSpaceNumber, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(totalSpaceTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE)) - .addContainerGap()) - ); - totalSpaceUsedPanelLayout.setVerticalGroup( - totalSpaceUsedPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(totalSpaceUsedPanelLayout.createSequentialGroup() - .addComponent(totalSpaceTitleLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(totalSpaceNumber, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 31, Short.MAX_VALUE)) - ); - - javax.swing.GroupLayout panelDashboardLayout = new javax.swing.GroupLayout(panelDashboard); - panelDashboard.setLayout(panelDashboardLayout); - panelDashboardLayout.setHorizontalGroup( - panelDashboardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelDashboardLayout.createSequentialGroup() - .addGap(52, 52, 52) - .addGroup(panelDashboardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelDashboardLayout.createSequentialGroup() - .addComponent(totalBackupsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(totalBackupConfigurationsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(totalSpaceUsedPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 228, Short.MAX_VALUE) - .addGroup(panelDashboardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(chart2, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addComponent(chart1, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addComponent(chart2TitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addComponent(chart1TitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGap(16, 16, 16)) - ); - panelDashboardLayout.setVerticalGroup( - panelDashboardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelDashboardLayout.createSequentialGroup() - .addGap(18, 18, 18) - .addGroup(panelDashboardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelDashboardLayout.createSequentialGroup() - .addGroup(panelDashboardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelDashboardLayout.createSequentialGroup() - .addComponent(chart1TitleLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(chart1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(totalBackupConfigurationsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(chart2TitleLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(chart2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(panelDashboardLayout.createSequentialGroup() - .addComponent(totalBackupsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(20, 20, 20) - .addComponent(totalSpaceUsedPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(34, Short.MAX_VALUE)) - ); - - layeredCardPanel.add(panelDashboard, "Dashboard"); - - getContentPane().add(layeredCardPanel, java.awt.BorderLayout.CENTER); - - jMenu1.setText("File"); - - MenuNew.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.event.InputEvent.CTRL_DOWN_MASK)); - MenuNew.setText("New"); - MenuNew.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuNewActionPerformed(evt); - } - }); - jMenu1.add(MenuNew); - - MenuSave.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_DOWN_MASK)); - MenuSave.setText("Save"); - MenuSave.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuSaveActionPerformed(evt); - } - }); - jMenu1.add(MenuSave); - - MenuSaveWithName.setText("Save with name"); - MenuSaveWithName.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuSaveWithNameActionPerformed(evt); - } - }); - jMenu1.add(MenuSaveWithName); - jMenu1.add(jSeparator4); - - MenuImport.setText("Import backup list"); - jMenu1.add(MenuImport); - - MenuExport.setText("Export backup list"); - 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.setText("Clear"); - MenuClear.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuClearActionPerformed(evt); - } - }); - jMenu1.add(MenuClear); - - MenuHistory.setText("History"); - MenuHistory.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuHistoryActionPerformed(evt); - } - }); - jMenu1.add(MenuHistory); - - jMenuBar1.add(jMenu1); - - jMenu2.setText("Options"); - - MenuPreferences.setText("Preferences"); - MenuPreferences.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuPreferencesActionPerformed(evt); - } - }); - jMenu2.add(MenuPreferences); - - MenuQuit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_DOWN_MASK)); - MenuQuit.setText("Quit"); - MenuQuit.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuQuitActionPerformed(evt); - } - }); - jMenu2.add(MenuQuit); - - jMenuBar1.add(jMenu2); - - jMenu3.setText("About"); - - MenuWebsite.setText("Website"); - MenuWebsite.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuWebsiteActionPerformed(evt); - } - }); - jMenu3.add(MenuWebsite); - - MenuInfoPage.setText("Info"); - MenuInfoPage.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuInfoPageActionPerformed(evt); - } - }); - jMenu3.add(MenuInfoPage); - - MenuShare.setText("Share"); - MenuShare.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuShareActionPerformed(evt); - } - }); - jMenu3.add(MenuShare); - - MenuDonate.setText("Donate"); - - MenuPaypalDonate.setText("Paypal"); - MenuPaypalDonate.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuPaypalDonateActionPerformed(evt); - } - }); - MenuDonate.add(MenuPaypalDonate); - - MenuBuyMeACoffeDonate.setText("Buy me a coffe"); - MenuBuyMeACoffeDonate.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuBuyMeACoffeDonateActionPerformed(evt); - } - }); - MenuDonate.add(MenuBuyMeACoffeDonate); - - jMenu3.add(MenuDonate); - - jMenuBar1.add(jMenu3); - - jMenu5.setText("Help"); - - MenuBugReport.setText("Report a bug"); - MenuBugReport.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuBugReportActionPerformed(evt); - } - }); - jMenu5.add(MenuBugReport); - - MenuSupport.setText("Support"); - MenuSupport.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - MenuSupportActionPerformed(evt); - } - }); - jMenu5.add(MenuSupport); - - jMenuBar1.add(jMenu5); - - setJMenuBar(jMenuBar1); - - pack(); - setLocationRelativeTo(null); - }// </editor-fold>//GEN-END:initComponents - - private void MenuQuitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuQuitActionPerformed - BackupMenuController.menuItemQuit(observer, this); - }//GEN-LAST:event_MenuQuitActionPerformed - - private void MenuHistoryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuHistoryActionPerformed - BackupMenuController.menuItemHistory(); - }//GEN-LAST:event_MenuHistoryActionPerformed - - private void MenuClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuClearActionPerformed - }//GEN-LAST:event_MenuClearActionPerformed - - private void MenuSaveWithNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuSaveWithNameActionPerformed - }//GEN-LAST:event_MenuSaveWithNameActionPerformed - - private void MenuSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuSaveActionPerformed - }//GEN-LAST:event_MenuSaveActionPerformed - - private void MenuNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuNewActionPerformed - BackupMenuController.menuItemNew(progressBar, this); - }//GEN-LAST:event_MenuNewActionPerformed - - private void EditPoputItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_EditPoputItemActionPerformed - BackupPopupController.popupItemEditBackupName(selectedRow, backupTable, backups, this); - }//GEN-LAST:event_EditPoputItemActionPerformed - - private void DeletePopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_DeletePopupItemActionPerformed - BackupPopupController.popupItemDelete(selectedRow, backupTable); - }//GEN-LAST:event_DeletePopupItemActionPerformed - - private void tableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tableMouseClicked - selectedRow = table.rowAtPoint(evt.getPoint()); // get index of the row - - if (selectedRow == -1) { // if clicked outside valid rows - table.clearSelection(); // deselect any selected row - detailsLabel.setText(""); // clear the label - } else { - String backupName = (String) backupTable.getValueAt(selectedRow, 0); - logger.debug("Selected backup: " + backupName); - - if (SwingUtilities.isRightMouseButton(evt)) { - logger.debug("Right click on row: " + selectedRow); - AutoBackupMenuItem.setSelected(backupManagerController.isAutomaticBackup(backupName)); - table.setRowSelectionInterval(selectedRow, selectedRow); // select clicked row - TablePopup.show(evt.getComponent(), evt.getX(), evt.getY()); - - boolean isRunning = backupManagerController.isBackupRunning(backupName); - - DeletePopupItem.setEnabled(!isRunning); - interruptBackupPopupItem.setEnabled(isRunning); - } - - // Handling left mouse button double-click - else if (SwingUtilities.isLeftMouseButton(evt) && evt.getClickCount() == 2) { - backupManagerController.openBackup(backupName, this); - } - - else if (SwingUtilities.isLeftMouseButton(evt)) { - String details = backupManagerController.getBackupDetails(backupName); - detailsLabel.setText(details); - } - } - }//GEN-LAST:event_tableMouseClicked - - private void DuplicatePopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_DuplicatePopupItemActionPerformed - BackupPopupController.popupItemDuplicateBackup(selectedRow, backupTable); - }//GEN-LAST:event_DuplicatePopupItemActionPerformed - - private void RunBackupPopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_RunBackupPopupItemActionPerformed - BackupPopupController.popupItemRunBackup(selectedRow, backupTable, backups, interruptBackupPopupItem, RunBackupPopupItem); - }//GEN-LAST:event_RunBackupPopupItemActionPerformed - - private void CopyBackupNamePopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CopyBackupNamePopupItemActionPerformed - BackupPopupController.popupItemCopyBackupName(selectedRow, backupTable, backups); - }//GEN-LAST:event_CopyBackupNamePopupItemActionPerformed - - private void CopyInitialPathPopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CopyInitialPathPopupItemActionPerformed - BackupPopupController.popupItemCopyInitialPath(selectedRow, backupTable, backups); - }//GEN-LAST:event_CopyInitialPathPopupItemActionPerformed - - private void CopyDestinationPathPopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CopyDestinationPathPopupItemActionPerformed - BackupPopupController.popupItemCopyDestinationPath(selectedRow, backupTable, backups); - }//GEN-LAST:event_CopyDestinationPathPopupItemActionPerformed - - private void AutoBackupMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_AutoBackupMenuItemActionPerformed - BackupPopupController.popupItemAutoBackup(selectedRow, backupTable, backups, AutoBackupMenuItem); - }//GEN-LAST:event_AutoBackupMenuItemActionPerformed - - private void OpenInitialFolderItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_OpenInitialFolderItemActionPerformed - BackupPopupController.popupItemOpenInitialPath(selectedRow, backupTable, backups); - }//GEN-LAST:event_OpenInitialFolderItemActionPerformed - - private void OpenInitialDestinationItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_OpenInitialDestinationItemActionPerformed - BackupPopupController.popupItemOpenDestinationPath(selectedRow, backupTable, backups); - }//GEN-LAST:event_OpenInitialDestinationItemActionPerformed - - private void renamePopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_renamePopupItemActionPerformed - BackupPopupController.popupItemRenameBackup(selectedRow, backupTable, backups); - }//GEN-LAST:event_renamePopupItemActionPerformed - - private void MenuPaypalDonateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuPaypalDonateActionPerformed - BackupMenuController.menuItemDonateViaPaypal(); - }//GEN-LAST:event_MenuPaypalDonateActionPerformed - - private void MenuBugReportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuBugReportActionPerformed - BackupMenuController.menuItemBugReport(); - }//GEN-LAST:event_MenuBugReportActionPerformed - - private void MenuShareActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuShareActionPerformed - BackupMenuController.menuItemShare(); - }//GEN-LAST:event_MenuShareActionPerformed - - private void MenuWebsiteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuWebsiteActionPerformed - BackupMenuController.menuItemWebsite(); - }//GEN-LAST:event_MenuWebsiteActionPerformed - - private void MenuSupportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuSupportActionPerformed - BackupMenuController.menuItemSupport(); - }//GEN-LAST:event_MenuSupportActionPerformed - - private void MenuInfoPageActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuInfoPageActionPerformed - BackupMenuController.menuItemInfoPage(); - }//GEN-LAST:event_MenuInfoPageActionPerformed - - private void MenuPreferencesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuPreferencesActionPerformed - BackupMenuController.menuItemOpenPreferences(this); - }//GEN-LAST:event_MenuPreferencesActionPerformed - - private void interruptBackupPopupItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interruptBackupPopupItemActionPerformed - BackupPopupController.popupItemInterrupt(selectedRow, backupTable, backups, interruptBackupPopupItem, RunBackupPopupItem); - }//GEN-LAST:event_interruptBackupPopupItemActionPerformed - - private void exportAsCsvBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportAsCsvBtnActionPerformed - ExportManager.exportAsCSV(new ArrayList<>(backups), ConfigurationBackup.getCSVHeader()); - }//GEN-LAST:event_exportAsCsvBtnActionPerformed - - private void exportAsPdfBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportAsPdfBtnActionPerformed - ExportManager.exportAsPDF(new ArrayList<>(backups), ConfigurationBackup.getCSVHeader()); - }//GEN-LAST:event_exportAsPdfBtnActionPerformed - - private void addBackupEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addBackupEntryButtonActionPerformed - BackupHelper.newBackup(progressBar, this); - }//GEN-LAST:event_addBackupEntryButtonActionPerformed - - private void MenuBuyMeACoffeDonateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MenuBuyMeACoffeDonateActionPerformed - BackupMenuController.menuItemDonateViaBuymeacoffe(); - }//GEN-LAST:event_MenuBuyMeACoffeDonateActionPerformed - - private void researchFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_researchFieldKeyReleased - String research = researchField.getText(); - backupManagerController.researchInTable(research); - }//GEN-LAST:event_researchFieldKeyReleased - - private void setTranslations() { - // update table translations - if (backups != null) - displayBackupList(); - - // general - jLabel3.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.VERSION) + " " + ConfigKey.VERSION.getValue()); - - // menu - jMenu1.setText(TranslationCategory.MENU.getTranslation(TranslationKey.FILE)); - jMenu2.setText(TranslationCategory.MENU.getTranslation(TranslationKey.OPTIONS)); - jMenu3.setText(TranslationCategory.MENU.getTranslation(TranslationKey.ABOUT)); - jMenu5.setText(TranslationCategory.MENU.getTranslation(TranslationKey.HELP)); - - // menu items - MenuBugReport.setText(TranslationCategory.MENU.getTranslation(TranslationKey.BUG_REPORT)); - MenuClear.setText(TranslationCategory.MENU.getTranslation(TranslationKey.CLEAR)); - MenuDonate.setText(TranslationCategory.MENU.getTranslation(TranslationKey.DONATE)); - MenuHistory.setText(TranslationCategory.MENU.getTranslation(TranslationKey.HISTORY)); - MenuInfoPage.setText(TranslationCategory.MENU.getTranslation(TranslationKey.INFO_PAGE)); - MenuNew.setText(TranslationCategory.MENU.getTranslation(TranslationKey.NEW)); - 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)); - - // backup list - ExportLabel.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.EXPORT_AS)); - addBackupEntryButton.setToolTipText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.ADD_BACKUP_TOOLTIP)); - exportAsPdfBtn.setToolTipText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.EXPORT_AS_PDF_TOOLTIP)); - exportAsCsvBtn.setToolTipText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.EXPORT_AS_CSV_TOOLTIP)); - researchField.setToolTipText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.RESEARCH_BAR_TOOLTIP)); - researchField.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.RESEARCH_BAR_PLACEHOLDER)); - - // popup - CopyBackupNamePopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.COPY_BACKUP_NAME_POPUP)); - CopyDestinationPathPopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.COPY_DESTINATION_PATH_BACKUP)); - RunBackupPopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.SINGLE_BACKUP_POPUP)); - CopyInitialPathPopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.COPY_INITIAL_PATH_POPUP)); - DeletePopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.DELETE_POPUP)); - interruptBackupPopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.INTERRUPT_POPUP)); - DuplicatePopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.DUPLICATE_POPUP)); - EditPoputItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.EDIT_POPUP)); - OpenInitialDestinationItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.OPEN_DESTINATION_FOLDER_POPUP)); - OpenInitialFolderItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.OPEN_INITIAL_FOLDER_POPUP)); - renamePopupItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.RENAME_BACKUP_POPUP)); - jMenu4.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.COPY_TEXT_POPUP)); - AutoBackupMenuItem.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.AUTO_BACKUP_POPUP)); - Backup.setText(TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.BACKUP_POPUP)); - } - - private String[] getColumnTranslations() { - String[] columnNames = { - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.BACKUP_NAME_COLUMN), - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.INITIAL_PATH_COLUMN), - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.DESTINATION_PATH_COLUMN), - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.LAST_BACKUP_COLUMN), - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.AUTOMATIC_BACKUP_COLUMN), - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.NEXT_BACKUP_DATE_COLUMN), - TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.TIME_INTERVAL_COLUMN) - }; - return columnNames; - } - - private void initializeTable() { - backups = BackupConfigurationRepository.getBackupList(); - displayBackupList(); - } - - private void setSvgImages() { - exportAsCsvBtn.setSvgImage("res/img/csv.svg", 30, 30); - exportAsPdfBtn.setSvgImage("res/img/pdf.svg", 30, 30); - addBackupEntryButton.setSvgImage("res/img/add.svg", 30, 30); - MenuSave.setSvgImage("res/img/save.svg", 16, 16); - MenuSaveWithName.setSvgImage("res/img/save_as.svg", 16, 16); - MenuImport.setSvgImage("res/img/import.svg", 16, 16); - MenuExport.setSvgImage("res/img/export.svg", 16, 16); - MenuNew.setSvgImage("res/img/new_file.svg", 16, 16); - MenuBugReport.setSvgImage("res/img/bug.svg", 16, 16); - MenuClear.setSvgImage("res/img/clear.svg", 16, 16); - MenuHistory.setSvgImage("res/img/history.svg", 16, 16); - MenuDonate.setSvgImage("res/img/donate.svg", 16, 16); - MenuPaypalDonate.setSvgImage("res/img/paypal.svg", 16, 16); - MenuBuyMeACoffeDonate.setSvgImage("res/img/buymeacoffee.svg", 16, 16); - MenuPreferences.setSvgImage("res/img/settings.svg", 16, 16); - MenuShare.setSvgImage("res/img/share.svg", 16, 16); - MenuSupport.setSvgImage("res/img/support.svg", 16, 16); - MenuWebsite.setSvgImage("res/img/website.svg", 16, 16); - MenuQuit.setSvgImage("res/img/quit.svg", 16, 16); - MenuInfoPage.setSvgImage("res/img/info.svg", 16, 16); - } - - private void initializeMenuItems() { - JSONConfigReader config = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); - MenuBugReport.setVisible(config.isMenuItemEnabled(MenuItems.BugReport.name())); - MenuPreferences.setVisible(config.isMenuItemEnabled(MenuItems.Preferences.name())); - MenuClear.setVisible(config.isMenuItemEnabled(MenuItems.Clear.name())); - MenuDonate.setVisible(config.isMenuItemEnabled(MenuItems.Donate.name())); - MenuPaypalDonate.setVisible(config.isMenuItemEnabled(MenuItems.PaypalDonate.name())); - MenuBuyMeACoffeDonate.setVisible(config.isMenuItemEnabled(MenuItems.BuymeacoffeeDonate.name())); - MenuHistory.setVisible(config.isMenuItemEnabled(MenuItems.History.name())); - MenuInfoPage.setVisible(config.isMenuItemEnabled(MenuItems.InfoPage.name())); - MenuNew.setVisible(config.isMenuItemEnabled(MenuItems.New.name())); - MenuQuit.setVisible(config.isMenuItemEnabled(MenuItems.Quit.name())); - MenuSave.setVisible(config.isMenuItemEnabled(MenuItems.Save.name())); - MenuSaveWithName.setVisible(config.isMenuItemEnabled(MenuItems.SaveWithName.name())); - 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())); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JCheckBoxMenuItem AutoBackupMenuItem; - private javax.swing.JMenu Backup; - private javax.swing.JMenuItem CopyBackupNamePopupItem; - private javax.swing.JMenuItem CopyDestinationPathPopupItem; - private javax.swing.JMenuItem CopyInitialPathPopupItem; - private javax.swing.JMenuItem DeletePopupItem; - private javax.swing.JMenuItem DuplicatePopupItem; - private javax.swing.JMenuItem EditPoputItem; - private javax.swing.JLabel ExportLabel; - private backupmanager.svg.SVGMenuItem MenuBugReport; - private backupmanager.svg.SVGMenuItem MenuBuyMeACoffeDonate; - private backupmanager.svg.SVGMenuItem MenuClear; - private backupmanager.svg.SVGMenu MenuDonate; - private backupmanager.svg.SVGMenuItem MenuExport; - private backupmanager.svg.SVGMenuItem MenuHistory; - private backupmanager.svg.SVGMenuItem MenuImport; - private backupmanager.svg.SVGMenuItem MenuInfoPage; - private backupmanager.svg.SVGMenuItem MenuNew; - private backupmanager.svg.SVGMenuItem MenuPaypalDonate; - private backupmanager.svg.SVGMenuItem MenuPreferences; - private backupmanager.svg.SVGMenuItem MenuQuit; - private backupmanager.svg.SVGMenuItem MenuSave; - private backupmanager.svg.SVGMenuItem MenuSaveWithName; - private backupmanager.svg.SVGMenuItem MenuShare; - private backupmanager.svg.SVGMenuItem MenuSupport; - private backupmanager.svg.SVGMenuItem MenuWebsite; - private javax.swing.JMenuItem OpenInitialDestinationItem; - private javax.swing.JMenuItem OpenInitialFolderItem; - private javax.swing.JMenuItem RunBackupPopupItem; - private javax.swing.JPopupMenu TablePopup; - private backupmanager.svg.SVGButton addBackupEntryButton; - private javax.swing.JPanel chart1; - private javax.swing.JLabel chart1TitleLabel; - private javax.swing.JPanel chart2; - private javax.swing.JLabel chart2TitleLabel; - private javax.swing.JLabel detailsLabel; - private backupmanager.svg.SVGButton exportAsCsvBtn; - private backupmanager.svg.SVGButton exportAsPdfBtn; - private javax.swing.JMenuItem interruptBackupPopupItem; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel3; - private javax.swing.JMenu jMenu1; - private javax.swing.JMenu jMenu2; - private javax.swing.JMenu jMenu3; - private javax.swing.JMenu jMenu4; - private javax.swing.JMenu jMenu5; - private javax.swing.JMenuBar jMenuBar1; - private javax.swing.JScrollPane jScrollPane1; - 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.JLayeredPane layeredCardPanel; - private javax.swing.JPanel panelBackupList; - private javax.swing.JPanel panelDashboard; - private javax.swing.JPanel panelVersion; - private javax.swing.JMenuItem renamePopupItem; - private javax.swing.JTextField researchField; - private javax.swing.JTable table; - private javax.swing.JLabel totalBackupConfigurationsNumber; - private javax.swing.JPanel totalBackupConfigurationsPanel; - private javax.swing.JLabel totalBackupConfigurationsTitleLabel; - private javax.swing.JLabel totalBackupsNumber; - private javax.swing.JPanel totalBackupsPanel; - private javax.swing.JLabel totalBackupsTitleLabel; - private javax.swing.JLabel totalSpaceNumber; - private javax.swing.JLabel totalSpaceTitleLabel; - private javax.swing.JPanel totalSpaceUsedPanel; - // End of variables declaration//GEN-END:variables -} diff --git a/src/main/java/backupmanager/GUI/Controllers/BackupManagerController.java b/src/main/java/backupmanager/GUI/Controllers/BackupManagerController.java deleted file mode 100644 index 412014b0..00000000 --- a/src/main/java/backupmanager/GUI/Controllers/BackupManagerController.java +++ /dev/null @@ -1,166 +0,0 @@ -package backupmanager.GUI.Controllers; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import javax.swing.JOptionPane; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.Dialogs.EntryUserDialog; -import backupmanager.Email.EmailSender; -import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Entities.Confingurations; -import backupmanager.Entities.User; -import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.LanguagesEnum; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.BackupManagerGUI; -import static backupmanager.GUI.BackupManagerGUI.backups; -import backupmanager.Helpers.BackupHelper; -import static backupmanager.Helpers.BackupHelper.formatter; -import backupmanager.Services.BackupService; -import backupmanager.Table.BackupTable; -import backupmanager.Table.TableDataManager; -import backupmanager.database.Repositories.UserRepository; - -public class BackupManagerController { - - private static final Logger logger = LoggerFactory.getLogger(BackupManagerGUI.class); - private final BackupService backupService; - - public BackupManagerController(BackupService backupService) { - this.backupService = backupService; - } - - public void checkForFirstAccess(BackupManagerGUI mainGui) { - logger.debug("Checking for first access"); - User user = UserRepository.getLastUser(); - - if (user == null) { - setLanguageBasedOnPcLanguage(mainGui); - createNewUser(mainGui); - } else { - logger.info("Current user: " + user.toString()); - } - } - - public int[] getScreenSize() { - 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())); - return new int[]{width, height}; - } - - public void researchInTable(String research) { - List<ConfigurationBackup> tempBackups = new ArrayList<>(); - research = research.toLowerCase(); - - for (ConfigurationBackup backup : backups) { - if (backup.getName().toLowerCase().contains(research) || - backup.getTargetPath().toLowerCase().contains(research) || - backup.getDestinationPath().toLowerCase().contains(research) || - (backup.getLastBackupDate() != null && backup.getLastBackupDate().toString().toLowerCase().contains(research)) || - (backup.getNextBackupDate() != null && backup.getNextBackupDate().toString().toLowerCase().contains(research)) || - (backup.getTimeIntervalBackup() != null && backup.getTimeIntervalBackup().toString().toLowerCase().contains(research))) { - tempBackups.add(backup); - } - } - - TableDataManager.updateTableWithNewBackupList(tempBackups, formatter); - } - - public String getBackupDetails(String backupName) { - return backupService.getBackupDetails(backupName); - } - - public void deleteBackups(List<String> names) { - backupService.deleteBackups(names); - } - - public boolean isBackupRunning(String name) { - return backupService.isRunning(name); - } - - public boolean isAutomaticBackup(String backupName) { - ConfigurationBackup backup = ConfigurationBackup.getBackupByName(backups, backupName); - return backup != null && backup.isAutomatic(); - } - - public void openBackup(String name, java.awt.Frame frame) { - logger.info("Double-click on row: " + name); - BackupHelper.openBackupByName(name, frame); - } - - public void handleEnterKeyPressOnTable(java.awt.Frame frame, BackupTable backupTable) { - int selectedRow = backupTable.getSelectedRow(); - if (selectedRow == -1) return; - - logger.debug("Enter key pressed on row: " + selectedRow); - BackupHelper.openBackupByName((String) backupTable.getValueAt(selectedRow, 0), frame); - - BackupHelper.openBackupEntryDialog(frame); - } - - public void handleDeleteKeyPressOnTable(BackupTable backupTable) { - int[] selectedRows = backupTable.getSelectedRows(); - if (selectedRows.length == 0) - return; - - logger.debug("Delete key pressed on rows: " + Arrays.toString(selectedRows)); - - int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_DELETION_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_DELETION_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); - if (response != JOptionPane.YES_OPTION) - return; - - for (int row : selectedRows) - BackupHelper.deleteBackup(row, backupTable, false); - } - - private void createNewUser(BackupManagerGUI mainGui) { - User newUser = null; - - while (newUser == null) { - newUser = openUserDialogAndObtainTheResult(mainGui); - } - - UserRepository.insertUser(newUser); - - sendRegistrationEmail(newUser); - } - - private void setLanguageBasedOnPcLanguage(BackupManagerGUI mainGui) { - Locale defaultLocale = Locale.getDefault(); - String language = defaultLocale.getLanguage(); - - logger.info("Setting default language to: " + language); - - switch (language) { - case "en" -> Confingurations.setLanguage(LanguagesEnum.ENG); - case "it" -> Confingurations.setLanguage(LanguagesEnum.ITA); - case "es" -> Confingurations.setLanguage(LanguagesEnum.ESP); - case "de" -> Confingurations.setLanguage(LanguagesEnum.DEU); - case "fr" -> Confingurations.setLanguage(LanguagesEnum.FRA); - default -> Confingurations.setLanguage(LanguagesEnum.ENG); - } - - mainGui.reloadPreferences(); - } - - private User openUserDialogAndObtainTheResult(BackupManagerGUI mainGui) { - EntryUserDialog userDialog = new EntryUserDialog(mainGui, true); - userDialog.setVisible(true); - return userDialog.getUser(); - } - - private void sendRegistrationEmail(User user) { - EmailSender.sendUserCreationEmail(user); - EmailSender.sendConfirmEmailToUser(user); - } -} diff --git a/src/main/java/backupmanager/GUI/Controllers/BackupMenuController.java b/src/main/java/backupmanager/GUI/Controllers/BackupMenuController.java deleted file mode 100644 index 21f84861..00000000 --- a/src/main/java/backupmanager/GUI/Controllers/BackupMenuController.java +++ /dev/null @@ -1,95 +0,0 @@ -package backupmanager.GUI.Controllers; - -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; -import java.io.IOException; - -import javax.swing.JOptionPane; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.Dialogs.PreferencesDialog; -import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.BackupManagerGUI; -import backupmanager.GUI.BackupProgressGUI; -import backupmanager.Helpers.BackupHelper; -import backupmanager.Managers.WebsiteManager; -import backupmanager.Services.BackupObserver; - -public class BackupMenuController { - - private static final Logger logger = LoggerFactory.getLogger(BackupMenuController.class); - - public static void menuItemDonateViaBuymeacoffe() { - logger.info("Event --> buymeacoffe donation"); - WebsiteManager.openWebSite(ConfigKey.DONATE_BUYMEACOFFE_LINK.getValue()); - } - - public static void menuItemDonateViaPaypal() { - logger.info("Event --> paypal donation"); - WebsiteManager.openWebSite(ConfigKey.DONATE_PAYPAL_LINK.getValue()); - } - - public static void menuItemOpenPreferences(BackupManagerGUI main) { - logger.info("Event --> opening preferences dialog"); - PreferencesDialog prefs = new PreferencesDialog(main, true, main); - prefs.setVisible(true); - } - - public static void menuItemInfoPage() { - logger.info("Event --> shard website"); - WebsiteManager.openWebSite(ConfigKey.INFO_PAGE_LINK.getValue()); - } - - public static void menuItemSupport() { - logger.info("Event --> support"); - WebsiteManager.sendEmail(); - } - - public static void menuItemWebsite() { - logger.info("Event --> shard website"); - WebsiteManager.openWebSite(ConfigKey.SHARD_WEBSITE.getValue()); - } - - public static void menuItemShare() { - logger.info("Event --> share"); - - // pop-up message - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.SHARE_LINK_COPIED_MESSAGE)); - - // copy link to the clipboard - StringSelection stringSelectionObj = new StringSelection(ConfigKey.SHARE_LINK.getValue()); - Clipboard clipboardObj = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboardObj.setContents(stringSelectionObj, null); - } - - public static void menuItemBugReport() { - logger.info("Event --> bug report"); - WebsiteManager.openWebSite(ConfigKey.ISSUE_PAGE_LINK.getValue()); - } - - public static void menuItemNew(BackupProgressGUI progressBar, BackupManagerGUI main) { - BackupHelper.newBackup(progressBar, main); - } - - public static void menuItemHistory() { - logger.info("Event --> history"); - try { - logger.debug("Opening log file with path: " + ConfigKey.LOG_DIRECTORY_STRING.getValue() + ConfigKey.LOG_FILE_STRING.getValue()); - new ProcessBuilder("notepad.exe", ConfigKey.LOG_DIRECTORY_STRING.getValue() + ConfigKey.LOG_FILE_STRING.getValue()).start(); - } catch (IOException e) { - logger.error("Error opening history file: " + e.getMessage(), e); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_OPEN_HISTORY_FILE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - } - } - - public static void menuItemQuit(BackupObserver observer, BackupManagerGUI main) { - logger.info("Event --> exit"); - observer.stop(); - System.exit(0); - } -} diff --git a/src/main/java/backupmanager/GUI/Controllers/BackupPopupController.java b/src/main/java/backupmanager/GUI/Controllers/BackupPopupController.java deleted file mode 100644 index a42d9e6f..00000000 --- a/src/main/java/backupmanager/GUI/Controllers/BackupPopupController.java +++ /dev/null @@ -1,242 +0,0 @@ -package backupmanager.GUI.Controllers; - -import java.awt.Desktop; -import java.awt.Toolkit; -import java.awt.datatransfer.StringSelection; -import java.io.File; -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.BackupOperations; -import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Entities.ZippingContext; -import backupmanager.Enums.BackupTriggerType; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.BackupManagerGUI; -import backupmanager.GUI.BackupProgressGUI; -import backupmanager.Helpers.BackupHelper; -import backupmanager.Managers.ExceptionManager; -import backupmanager.Table.BackupTable; -import backupmanager.Table.TableDataManager; -import backupmanager.database.Repositories.BackupConfigurationRepository; - -public class BackupPopupController { - - private static final Logger logger = LoggerFactory.getLogger(BackupPopupController.class); - - public static void popupItemInterrupt(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups, JMenuItem interruptBackupPopupItem, JMenuItem RunBackupPopupItem) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - ZippingContext context = ZippingContext.create(backup, null, backupTable, BackupManagerGUI.progressBar, interruptBackupPopupItem, RunBackupPopupItem); - BackupOperations.interruptBackupProcess(context); - } - } - - public static void popupItemRenameBackup(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - renameBackup(backups, backup); - } - } - - public static void popupItemOpenDestinationPath(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - openFolder(backup.getDestinationPath()); - } - } - - public static void popupItemOpenInitialPath(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - openFolder(backup.getTargetPath()); - } - } - - public static void popupItemAutoBackup(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups, JCheckBoxMenuItem autoBackupMenuItem) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - autoBackupMenuItem.setSelected(!backup.isAutomatic()); - BackupHelper.toggleAutomaticBackup(backup); - } - } - - public static void popupItemCopyDestinationPath(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - StringSelection selection = new StringSelection(backup.getDestinationPath()); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); - } - } - - public static void popupItemCopyInitialPath(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - StringSelection selection = new StringSelection(backup.getTargetPath()); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); - } - } - - public static void popupItemCopyBackupName(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - StringSelection selection = new StringSelection(backup.getName()); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); - } - } - - public static void popupItemRunBackup(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups, JMenuItem interruptBackupPopupItem, JMenuItem RunBackupPopupItem) { - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(backups, selectedRow, backupTable); - - BackupManagerGUI.progressBar = new BackupProgressGUI(backup.getTargetPath(), backup.getDestinationPath()); - - ZippingContext context = ZippingContext.create(backup, null, backupTable, BackupManagerGUI.progressBar, interruptBackupPopupItem, RunBackupPopupItem); - BackupOperations.singleBackup(context, BackupTriggerType.USER); - } - } - - public static void popupItemEditBackupName(int selectedRow, BackupTable backupTable, List<ConfigurationBackup> backups, BackupManagerGUI main) { - if (selectedRow != -1) { - // get correct backup - String backupName = (String) backupTable.getValueAt(selectedRow, 0); - ConfigurationBackup backup = ConfigurationBackup.getBackupByName(new ArrayList<>(backups), backupName); - - logger.info("Edit row : " + selectedRow); - BackupHelper.openBackupById(backup.getId(), main); - } - } - - public static void popupItemDuplicateBackup(int selectedRow, BackupTable backupTable) { - logger.info("Event --> duplicating backup"); - - if (selectedRow != -1) { - ConfigurationBackup backup = getBackupByName(selectedRow, backupTable); - - LocalDateTime dateNow = LocalDateTime.now(); - ConfigurationBackup newBackup = new ConfigurationBackup( - backup.getName() + "_copy", - backup.getTargetPath(), - backup.getDestinationPath(), - null, - backup.isAutomatic(), - backup.getNextBackupDate(), - backup.getTimeIntervalBackup(), - backup.getNotes(), - dateNow, - dateNow, - 0, - backup.getMaxToKeep() - ); - - BackupConfigurationRepository.insertBackup(newBackup); - - List<ConfigurationBackup> backups = BackupHelper.getBackupList(); - - if (BackupManagerGUI.model != null) - TableDataManager.updateTableWithNewBackupList(backups, BackupHelper.formatter); - } - } - - public static void popupItemDelete(int selectedRow, BackupTable backupTable) { - BackupHelper.deleteBackup(selectedRow, backupTable); - } - - private static ConfigurationBackup getBackupByName(int selectedRow, BackupTable backupTable) { - String backupName = (String) backupTable.getValueAt(selectedRow, 0); - return BackupConfigurationRepository.getBackupByName(backupName); - } - - private static ConfigurationBackup getBackupByName(List<ConfigurationBackup> backups, int selectedRow, BackupTable backupTable) { - String backupName = (String) backupTable.getValueAt(selectedRow, 0); - return ConfigurationBackup.getBackupByName(backups, backupName); - } - - private static void renameBackup(List<ConfigurationBackup> backups, ConfigurationBackup backup) { - logger.info("Event --> backup renaming"); - - String backupName = getBackupNameFromInputDialog(backups, backup.getName(), false); - if (backupName == null || backupName.isEmpty()) return; - - backup.setName(backupName); - backup.setLastUpdateDate(LocalDateTime.now()); - BackupHelper.updateBackup(backup); - } - - private static String getBackupNameFromInputDialog(List<ConfigurationBackup> backups, String oldName, boolean canOverwrite) { - while (true) { - String backupName = JOptionPane.showInputDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.BACKUP_NAME_INPUT), oldName); - - // If the user cancels the operation - if (backupName == null || backupName.trim().isEmpty()) { - return null; - } - - Optional<ConfigurationBackup> existingBackup = backups.stream() - .filter(b -> b.getName().equals(backupName)) - .findFirst(); - - if (existingBackup.isPresent()) { - if (canOverwrite) { - int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.DUPLICATED_BACKUP_NAME_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - - if (response == JOptionPane.YES_OPTION) { - backups.remove(existingBackup.get()); - return backupName; - } - } else { - logger.warn("Backup name '{}' is already in use", backupName); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.BACKUP_NAME_ALREADY_USED_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - } - } else { - return backupName; // Return valid name - } - } - } - - private static void openFolder(String path) { - logger.info("Event --> opening folder"); - - File folder = new File(path); - - // if the object is a file i want to obtain the folder that contains that file - if (folder.exists() && folder.isFile()) { - folder = folder.getParentFile(); - } - - if (folder.exists() && folder.isDirectory()) { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - try { - desktop.open(folder); - } catch (IOException ex) { - logger.error("An error occurred: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - } else { - logger.warn("Desktop not supported on this operating system"); - } - } else { - logger.warn("The folder does not exist or is invalid"); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_FOLDER_NOT_EXISTING), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - } - } -} diff --git a/src/main/java/backupmanager/Helpers/BackupHelper.java b/src/main/java/backupmanager/Helpers/BackupHelper.java index 3e5280e7..0e84dd98 100644 --- a/src/main/java/backupmanager/Helpers/BackupHelper.java +++ b/src/main/java/backupmanager/Helpers/BackupHelper.java @@ -1,5 +1,7 @@ package backupmanager.Helpers; +import java.awt.Component; +import java.io.File; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @@ -10,19 +12,18 @@ import org.slf4j.LoggerFactory; import backupmanager.BackupOperations; -import backupmanager.Dialogs.BackupEntryDialog; -import backupmanager.Dialogs.TimePicker; +import backupmanager.Entities.BackupRequest; import backupmanager.Entities.ConfigurationBackup; import backupmanager.Entities.TimeInterval; import backupmanager.Enums.BackupStatus; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.BackupManagerGUI; -import backupmanager.GUI.BackupProgressGUI; -import backupmanager.Table.BackupTable; -import backupmanager.Table.TableDataManager; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Exceptions.BackupDeletionException; +import backupmanager.Utils.ModalUtils; import backupmanager.database.Repositories.BackupConfigurationRepository; import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.gui.simple.TimePickerDialog; +import raven.modal.component.SimpleModalBorder; public class BackupHelper { @@ -30,40 +31,31 @@ public class BackupHelper { public static final DateTimeFormatter dateForfolderNameFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH-mm-ss"); public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); - public static void openBackupById(int id, java.awt.Frame frame) { - logger.info("Event --> opening backup"); - - ConfigurationBackup backup = BackupConfigurationRepository.getBackupById(id); - - BackupEntryDialog dialog = new BackupEntryDialog(frame, false, backup); - dialog.setVisible(true); - } - - public static void newBackup(BackupProgressGUI progressBar, java.awt.Frame frame) { + public static void newBackup(ConfigurationBackup backup) { logger.info("Event --> new backup"); - - BackupEntryDialog dialog = new BackupEntryDialog(frame, false); - dialog.setVisible(true); + BackupConfigurationRepository.insertBackup(backup); } - public static void newBackup(ConfigurationBackup backup) { - BackupConfigurationRepository.insertBackup(backup); + public static boolean deleteBackupWithConfirmition(ConfigurationBackup backup) throws BackupDeletionException { + logger.info("Event --> deleting backup request with confirmation for backup: " + backup.getName()); - updateBackupTable(); + int response = JOptionPane.showConfirmDialog(null, Translations.get(TKey.CONFIRMATION_MESSAGE_BEFORE_DELETE_BACKUP), Translations.get(TKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + if (response == JOptionPane.YES_OPTION) { + return BackupHelper.deleteBackup(backup); + } + return false; } - public static void deleteBackup(int selectedRow, BackupTable backupTable, boolean isConfermationRequired) { + public static boolean deleteBackup(String backupName) throws BackupDeletionException { logger.info("Event --> deleting backup"); + ConfigurationBackup backup = ConfigurationBackup.getBackupByName(backupName); + return deleteBackup(backup); + } - if (isConfermationRequired) { - int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_MESSAGE_BEFORE_DELETE_BACKUP), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); - if (response != JOptionPane.YES_OPTION) { - return; - } - } - - String backupName = (String) backupTable.getValueAt(selectedRow, 0); - removeBackup(backupName); + public static boolean deleteBackup(ConfigurationBackup backup) throws BackupDeletionException { + logger.info("Event --> deleting backup" + backup.getName()); + BackupConfigurationRepository.deleteBackup(backup.getId()); + return true; } public static void updateBackup(ConfigurationBackup updatedBackup) { @@ -75,47 +67,34 @@ public static void updateBackup(ConfigurationBackup updatedBackup) { logger.info("Updating backup: " + updatedBackup.getName()); BackupConfigurationRepository.updateBackup(updatedBackup); } - - updateBackupTable(); } public static List<ConfigurationBackup> getBackupList() { List<ConfigurationBackup> backups = BackupConfigurationRepository.getBackupList(); - BackupManagerGUI.backups = backups; // i have to keep update also the backup list in the main panel return backups; } - public static TimeInterval openTimePicker(java.awt.Dialog parent, TimeInterval time) { - TimePicker picker = new TimePicker(parent, time, true); - picker.setVisible(true); - return picker.getTimeInterval(); + public static TimeInterval openTimePicker() { + return openTimePicker(new TimePickerDialog(null)); } - public static void showMessageActivationAutoBackup(TimeInterval timeInterval, String startPath, String destinationPath) { - String from = TranslationCategory.GENERAL.getTranslation(TranslationKey.FROM); - String to = TranslationCategory.GENERAL.getTranslation(TranslationKey.TO); - String activated = TranslationCategory.DIALOGS.getTranslation(TranslationKey.AUTO_BACKUP_ACTIVATED_MESSAGE); - String setted = TranslationCategory.DIALOGS.getTranslation(TranslationKey.SETTED_EVERY_MESSAGE); - String days = TranslationCategory.DIALOGS.getTranslation(TranslationKey.DAYS_MESSAGE); - - JOptionPane.showMessageDialog(null, - activated + "\n\t" + from + ": " + startPath + "\n\t" + to + ": " - + destinationPath + setted + " " + timeInterval.toString() + days, - "AutoBackup", 1); + public static TimeInterval openTimePicker(TimePickerDialog picker) { + picker.setVisible(true); + return picker.getResult(); } - public static void openBackupByName(String backupName, java.awt.Frame frame) { - logger.info("Event --> opening backup"); + public static void showMessageActivationAutoBackup(Component parent, TimeInterval timeInterval, String startPath, String destinationPath) { + String from = Translations.get(TKey.FROM); + String to = Translations.get(TKey.TO); + String activated = Translations.get(TKey.AUTO_BACKUP_ACTIVATED_MESSAGE); + String setted = Translations.get(TKey.SETTED_EVERY_MESSAGE); + String days = Translations.get(TKey.DAYS_MESSAGE); - ConfigurationBackup backup = BackupConfigurationRepository.getBackupByName(backupName); - - BackupEntryDialog dialog = new BackupEntryDialog(frame, false, backup); - dialog.setVisible(true); - } + String message = + activated + "\n\n" + from + ": " + startPath + "\n" + to + ": " + + destinationPath + "\n" + setted + " " + timeInterval.toString() + days; - public static void openBackupEntryDialog(java.awt.Frame frame) { - BackupEntryDialog dialog = new BackupEntryDialog(frame, false); - dialog.setVisible(true); + ModalUtils.showInfo(parent, Translations.get(TKey.AUTO_BACKUP_MESSAGE), message, SimpleModalBorder.CLOSE_OPTION); } public static LocalDateTime getNexDateBackup(TimeInterval timeInterval) { @@ -125,44 +104,16 @@ public static LocalDateTime getNexDateBackup(TimeInterval timeInterval) { .plusMinutes(timeInterval.minutes()); } - public static void deleteBackup(int selectedRow, BackupTable backupTable) { - logger.info("Event --> deleting backup"); - - if (selectedRow != -1) { - int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_MESSAGE_BEFORE_DELETE_BACKUP), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); - if (response == JOptionPane.YES_OPTION) { - String backupName = (String) backupTable.getValueAt(selectedRow, 0); - BackupHelper.removeBackup(backupName); - } - } - } - - public static void forceBackupTermination(int requestId) { - BackupRequestRepository.updateRequestStatusByRequestId(requestId, BackupStatus.TERMINATED); - updateBackupTable(); - } - - public static void removeBackup(String backupName) { - ConfigurationBackup backup = ConfigurationBackup.getBackupByName(backupName); - removeBackup(backup); - } - - private static void removeBackup(ConfigurationBackup backup) { - logger.info("Event --> removing backup" + backup.getName()); - BackupConfigurationRepository.deleteBackup(backup.getId()); - updateBackupTable(); - } - - private static void updateBackupTable() { - if (BackupManagerGUI.model != null) - TableDataManager.updateTableWithNewBackupList(getBackupList(), formatter); + public static void forceBackupTermination(BackupRequest request) { + BackupRequestRepository.updateRequestStatusByRequestId(request.backupRequestId(), BackupStatus.TERMINATED); + deletePartialBackup(request.outputPath()); } - public static ConfigurationBackup toggleAutomaticBackup(ConfigurationBackup backup) { + public static ConfigurationBackup toggleAutomaticBackup(Component parent, ConfigurationBackup backup) { logger.info("Event --> automatic backup"); if (backup.isAutomatic()) { - int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_MESSAGE_CANCEL_AUTO_BACKUP), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + int response = JOptionPane.showConfirmDialog(null, Translations.get(TKey.CONFIRMATION_MESSAGE_CANCEL_AUTO_BACKUP), Translations.get(TKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (response != JOptionPane.YES_OPTION) { return null; } @@ -187,7 +138,7 @@ public static ConfigurationBackup toggleAutomaticBackup(ConfigurationBackup back if (backup.getName() == null || backup.getName().isEmpty()) return null; // message - TimeInterval timeInterval = openTimePicker(null, null); + TimeInterval timeInterval = openTimePicker(); if (timeInterval == null) return null; //set date for next backup @@ -200,7 +151,7 @@ public static ConfigurationBackup toggleAutomaticBackup(ConfigurationBackup back logger.info("Automatic backup turned On and next date backup setted to {}", nextDateBackup); - showMessageActivationAutoBackup(timeInterval, backup.getTargetPath(), backup.getDestinationPath()); + showMessageActivationAutoBackup(parent, timeInterval, backup.getTargetPath(), backup.getDestinationPath()); updateBackup(backup); @@ -209,4 +160,39 @@ public static ConfigurationBackup toggleAutomaticBackup(ConfigurationBackup back return null; } + + private static boolean deletePartialBackup(String filePath) { + logger.info("Attempting to delete partial backup: " + filePath); + + if (filePath == null || filePath.isEmpty()) { + logger.warn("The file path is null or empty."); + return false; + } + + File file = new File(filePath); + + // Check if the file exists and is a valid file + if (file.exists()) { + if (file.isFile()) { + try { + if (file.delete()) { + logger.info("Partial backup deleted successfully: " + file.getName()); + return true; + } else { + logger.warn("Failed to delete partial backup (delete failed): " + file.getName()); + } + } catch (SecurityException e) { + logger.error("Security exception occurred while attempting to delete: " + file.getName(), e); + } catch (Exception e) { + logger.error("Unexpected error while attempting to delete: " + file.getName(), e); + } + } else { + logger.warn("The path points to a directory, not a file: " + filePath); + } + } else { + logger.warn("The file does not exist: " + filePath); + } + + return false; + } } diff --git a/src/main/java/backupmanager/Helpers/SqlHelper.java b/src/main/java/backupmanager/Helpers/SqlHelper.java index 0feb634d..2cce87d4 100644 --- a/src/main/java/backupmanager/Helpers/SqlHelper.java +++ b/src/main/java/backupmanager/Helpers/SqlHelper.java @@ -13,6 +13,13 @@ public static long toMilliseconds(LocalDateTime date) { return date != null ? date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : 0; } + public static long toMilliseconds(LocalDate date) { + return date == null ? 0L : + date.atStartOfDay(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli(); + } + public static long toMilliseconds(LocalDateTime date, LocalDateTime fallbackValue) { if (fallbackValue == null) throw new IllegalArgumentException("Cannot pass the fallback value as null"); return date != null ? date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : fallbackValue.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); diff --git a/src/main/java/backupmanager/Helpers/SubscriptionHelper.java b/src/main/java/backupmanager/Helpers/SubscriptionHelper.java new file mode 100644 index 00000000..7ab31e54 --- /dev/null +++ b/src/main/java/backupmanager/Helpers/SubscriptionHelper.java @@ -0,0 +1,50 @@ +package backupmanager.Helpers; + +import java.time.LocalDate; + +import backupmanager.Entities.Configurations; +import backupmanager.Entities.Subscription; +import backupmanager.Enums.SubscriptionStatus; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Json.JsonConfig; +import backupmanager.database.Repositories.SubscriptionRepository; + +public class SubscriptionHelper { + private static final JsonConfig configReader = JsonConfig.getInstance(); + + public static SubscriptionStatus getSubscriptionStatus() { + if (!Configurations.isSubscriptionNedded()) + return SubscriptionStatus.NONE; + + Subscription subscription = SubscriptionRepository.getAnySubscriptionValid(); + if (subscription == null) + return SubscriptionStatus.EXPIRED; + if (isSubscriptionExpiringSoon(subscription)) + return SubscriptionStatus.EXPIRATION; + + return SubscriptionStatus.ACTIVE; + } + + public static String getSubscriptionStatusTranslated(SubscriptionStatus status) { + String statusTranslation; + switch (status) { + case EXPIRED -> statusTranslation = Translations.get(TKey.SUBSCRIPTION_EXPIRED); + case ACTIVE -> statusTranslation = Translations.get(TKey.SUBSCRIPTION_ACTIVE); + case EXPIRATION -> statusTranslation = Translations.get(TKey.SUBSCRIPTION_EXPIRING); + default -> statusTranslation = ""; + } + return statusTranslation; + } + + public static Subscription getLastValidSubscription() { + return SubscriptionRepository.getAnySubscriptionValid(); + } + + private static boolean isSubscriptionExpiringSoon(Subscription subscription) { + int days = configReader.getConfigValue("SubscriptionWarningDays", 7); + LocalDate now = LocalDate.now(); + LocalDate endMinusDays = subscription.endDate().minusDays(days); + return now.isAfter(endMinusDays); + } +} diff --git a/src/main/java/backupmanager/Helpers/SubscriptionNotifier.java b/src/main/java/backupmanager/Helpers/SubscriptionNotifier.java index 338f8f4d..f97c541e 100644 --- a/src/main/java/backupmanager/Helpers/SubscriptionNotifier.java +++ b/src/main/java/backupmanager/Helpers/SubscriptionNotifier.java @@ -4,21 +4,21 @@ import javax.swing.JOptionPane; -import backupmanager.Controllers.TrayController; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.gui.Controllers.TrayController; public class SubscriptionNotifier { public static void showExpiringWarning(TrayController trayController) { - String title = TranslationCategory.SUBSCRIPTION.getTranslation(TranslationKey.SUBSCRIPTION_EXPIRING_TITLE); - String message = TranslationCategory.SUBSCRIPTION.getTranslation(TranslationKey.SUBSCRIPTION_EXPIRING_MESSAGE); + String title = Translations.get(TKey.SUBSCRIPTION_EXPIRING_TITLE); + String message = Translations.get(TKey.SUBSCRIPTION_EXPIRING_MESSAGE); showMessage(trayController, title, message, TrayIcon.MessageType.WARNING); } public static void showExpiredAlert(TrayController trayController) { - String title = TranslationCategory.SUBSCRIPTION.getTranslation(TranslationKey.SUBSCRIPTION_EXPIRED_TITLE); - String message = TranslationCategory.SUBSCRIPTION.getTranslation(TranslationKey.SUBSCRIPTION_EXPIRED_MESSAGE); + String title = Translations.get(TKey.SUBSCRIPTION_EXPIRED_TITLE); + String message = Translations.get(TKey.SUBSCRIPTION_EXPIRED_MESSAGE); showMessage(trayController, title, message, TrayIcon.MessageType.ERROR); } diff --git a/src/main/java/backupmanager/Json/JSONConfigReader.java b/src/main/java/backupmanager/Json/JsonConfig.java similarity index 75% rename from src/main/java/backupmanager/Json/JSONConfigReader.java rename to src/main/java/backupmanager/Json/JsonConfig.java index a120cbcb..492a933e 100644 --- a/src/main/java/backupmanager/Json/JSONConfigReader.java +++ b/src/main/java/backupmanager/Json/JsonConfig.java @@ -11,17 +11,39 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import backupmanager.Enums.ConfigKey; -public class JSONConfigReader { - private static final Logger logger = LoggerFactory.getLogger(JSONConfigReader.class); +// Singleton class +public class JsonConfig { + private static final Logger logger = LoggerFactory.getLogger(JsonConfig.class); + + // The field must be declared volatile so that double check lock would work correctly. + private static volatile JsonConfig instance; private final String filename; private final String directoryPath; private JsonObject config; - public JSONConfigReader(String filename, String directoryPath) { - this.filename = filename; - this.directoryPath = directoryPath; + // The approach taken here is called double-checked locking (DCL). It + // exists to prevent race condition between multiple threads that may + // attempt to get singleton instance at the same time, creating separate + // instances as a result. + public static JsonConfig getInstance() { + JsonConfig result = instance; + + if (result != null) + return result; + + synchronized (JsonConfig.class) { + if (instance == null) + instance = new JsonConfig(); + return instance; + } + } + + private JsonConfig() { + filename = ConfigKey.CONFIG_FILE_STRING.getValue(); + directoryPath = ConfigKey.CONFIG_DIRECTORY_STRING.getValue(); loadConfig(); // Load configuration at instantiation } diff --git a/src/main/java/backupmanager/MainApp.java b/src/main/java/backupmanager/MainApp.java index aa247b62..aa1dcf8d 100644 --- a/src/main/java/backupmanager/MainApp.java +++ b/src/main/java/backupmanager/MainApp.java @@ -1,31 +1,35 @@ package backupmanager; +import java.awt.Font; import java.io.IOException; import java.util.Arrays; +import javax.swing.UIManager; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import backupmanager.Controllers.AppController; -import backupmanager.Entities.Confingurations; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.fonts.roboto.FlatRobotoFont; +import com.formdev.flatlaf.util.FontUtils; + +import backupmanager.Entities.Configurations; import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum; -import backupmanager.GUI.BackupManagerGUI; import backupmanager.Managers.ExceptionManager; +import backupmanager.Managers.LanguageManager; +import backupmanager.Utils.AppPreferences; import backupmanager.database.Database; import backupmanager.database.DatabasePaths; import backupmanager.database.ProductionDatabaseInitializer; +import backupmanager.gui.Controllers.AppController; +import backupmanager.gui.frames.BackupManager; public class MainApp { private static final String CONFIG = "src/main/resources/res/config/config.json"; private static final Logger logger = LoggerFactory.getLogger(MainApp.class); public static void main(String[] args) { - ConfigKey.loadFromJson(CONFIG); - - databaseInitialization(); - - loadPreferredLanguage(); + dataInit(); boolean isBackgroundMode = isBackgroundMode(args); @@ -40,6 +44,17 @@ else if (!isBackgroundMode) { } } + private static void dataInit() { + ensureLogDirectory(); + ConfigKey.loadFromJson(CONFIG); + + databaseInitialization(); + + AppPreferences.init(); + LanguageManager.loadPreferredLanguage(); + Configurations.loadAllConfigurations(); + } + private static void databaseInitialization() { try { Database.init(DatabasePaths.getProductionDatabasePath()); @@ -50,15 +65,6 @@ private static void databaseInitialization() { } } - private static void loadPreferredLanguage() { - try { - Confingurations.loadAllConfigurations(); - TranslationLoaderEnum.loadTranslations(ConfigKey.LANGUAGES_DIRECTORY_STRING.getValue() + Confingurations.getLanguage().getFileName()); - } catch (IOException ex) { - logger.error("An error occurred during loading preferences: {}", ex.getMessage(), ex); - } - } - private static boolean isBackgroundMode(String[] args) { boolean isBackgroundMode = args.length > 0 && args[0].equalsIgnoreCase("--background"); @@ -70,6 +76,19 @@ private static boolean isBackgroundMode(String[] args) { return isBackgroundMode; } + private static void ensureLogDirectory() { + try { + String logDir = System.getProperty("user.home") + "/.backupmanager/logs"; + java.nio.file.Path path = java.nio.file.Paths.get(logDir); + java.nio.file.Files.createDirectories(path); + + logger.info("Log directory ensured at: {}", path.toAbsolutePath()); + } catch (IOException e) { + logger.error("Failed to create log directory", e); + throw new RuntimeException("Cannot initialize log directory", e); + } + } + private static void runBackgroundProcess() { try { AppController.startBackgroundProcess(); @@ -80,9 +99,19 @@ private static void runBackgroundProcess() { } private static void runGui() { - javax.swing.SwingUtilities.invokeLater(() -> { - BackupManagerGUI gui = new BackupManagerGUI(); - gui.showWindow(); + java.awt.EventQueue.invokeLater(() -> { + initLaf(); + BackupManager.getInstance().setVisible(true); }); } + + public static void initLaf() { + FlatRobotoFont.install(); + FlatLaf.registerCustomDefaultsSource(".themes"); + UIManager.put( + "defaultFont", + FontUtils.getCompositeFont(FlatRobotoFont.FAMILY, Font.PLAIN, 13) + ); + AppPreferences.setupLaf(); + } } diff --git a/src/main/java/backupmanager/Managers/ExceptionManager.java b/src/main/java/backupmanager/Managers/ExceptionManager.java index a6af2c8a..6f76aed4 100644 --- a/src/main/java/backupmanager/Managers/ExceptionManager.java +++ b/src/main/java/backupmanager/Managers/ExceptionManager.java @@ -13,24 +13,25 @@ import backupmanager.Email.EmailSender; import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Utils.ToastUtils; +import backupmanager.gui.menu.DrawerManager; public class ExceptionManager { private static final Logger logger = LoggerFactory.getLogger(ExceptionManager.class); public static void openExceptionMessage(String errorMessage, String stackTrace) { - Object[] options = {TranslationCategory.GENERAL.getTranslation(TranslationKey.CLOSE_BUTTON), TranslationCategory.DIALOGS.getTranslation(TranslationKey.EXCEPTION_MESSAGE_CLIPBOARD_BUTTON), TranslationCategory.DIALOGS.getTranslation(TranslationKey.EXCEPTION_MESSAGE_REPORT_BUTTON)}; + Object[] options = {Translations.get(TKey.CLOSE_BUTTON), Translations.get(TKey.EXCEPTION_MESSAGE_CLIPBOARD_BUTTON), Translations.get(TKey.EXCEPTION_MESSAGE_REPORT_BUTTON)}; - if (errorMessage == null) { + if (errorMessage == null) errorMessage = ""; - } stackTrace = !errorMessage.isEmpty() ? errorMessage + "\n" + stackTrace : errorMessage + stackTrace; EmailSender.sendErrorEmail("Critical Error Report", stackTrace, errorMessage); - String stackTraceMessage = TranslationCategory.DIALOGS.getTranslation(TranslationKey.EXCEPTION_MESSAGE_REPORT_MESSAGE) + "\n" + stackTrace; + String stackTraceMessage = Translations.get(TKey.EXCEPTION_MESSAGE_REPORT_MESSAGE) + "\n" + stackTrace; int choice; @@ -59,7 +60,7 @@ public static void openExceptionMessage(String errorMessage, String stackTrace) scrollPane.setPreferredSize(new Dimension(MAX_WIDTH, 300)); // Display the option dialog with the JScrollPane - String error = TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE); + String error = Translations.get(TKey.ERROR_GENERIC_TITLE); choice = JOptionPane.showOptionDialog( null, scrollPane, // The JScrollPane containing the error message @@ -75,9 +76,9 @@ public static void openExceptionMessage(String errorMessage, String stackTrace) StringSelection selection = new StringSelection(stackTrace); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); logger.info("Error text has been copied to the clipboard"); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.EXCEPTION_MESSAGE_CLIPBOARD_MESSAGE)); + ToastUtils.showInfo(DrawerManager.getInstance().getParent(), Translations.get(TKey.TOAST_ERROR_TEXT_CLIPBOARD)); } else if (choice == 2) { - WebsiteManager.openWebSite(ConfigKey.ISSUE_PAGE_LINK.getValue()); + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), ConfigKey.ISSUE_PAGE_LINK.getValue()); } } while (choice == 1 || choice == 2); } diff --git a/src/main/java/backupmanager/Managers/ExportManager.java b/src/main/java/backupmanager/Managers/ExportManager.java index 6b84bfd7..55bcc417 100644 --- a/src/main/java/backupmanager/Managers/ExportManager.java +++ b/src/main/java/backupmanager/Managers/ExportManager.java @@ -5,117 +5,25 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; -import java.util.ArrayList; +import java.util.List; +import javax.swing.JFrame; import javax.swing.JOptionPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.itextpdf.kernel.pdf.PdfDocument; -import com.itextpdf.kernel.pdf.PdfWriter; -import com.itextpdf.layout.Document; -import com.itextpdf.layout.element.Cell; -import com.itextpdf.layout.element.Paragraph; -import com.itextpdf.layout.element.Table; - import backupmanager.BackupOperations; import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Utils.ToastUtils; public class ExportManager { private static final Logger logger = LoggerFactory.getLogger(ExportManager.class); - public static void exportAsPDF(ArrayList<ConfigurationBackup> backups, String headers) { - logger.info("Exporting backups to PDF"); - - String path = BackupOperations.pathSearchWithFileChooser(false); - - if (path == null) { - logger.info("Exporting backups to PDF cancelled"); - return; - } - - String filename = JOptionPane.showInputDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.PDF_NAME_MESSAGE_INPUT)); - if (filename == null || filename.isEmpty()) { - logger.info("Exporting backups to PDF cancelled"); - return; - } - - // Validate filename - if (!filename.matches("[a-zA-Z0-9-_ ]+")) { - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_INVALID_FILENAME), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - logger.info("Exporting backups to PDF cancelled due to invalid file name"); - return; - } - - // Build full path - String fullPath = Paths.get(path, filename + ".pdf").toString(); - - // Check if the file exists - File file = new File(fullPath); - if (file.exists()) { - int overwrite = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.DUPLICATED_FILE_NAME_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION); - if (overwrite != JOptionPane.YES_OPTION) { - logger.info("Exporting backups to PDF cancelled by user (file exists)"); - return; - } - } - - try { - // Initialize PDF writer - PdfWriter writer = new PdfWriter(fullPath); - PdfDocument pdfDoc = new PdfDocument(writer); - Document document = new Document(pdfDoc); - - // insert pdf title - document.setFontSize(12f).setBold(); - - // Create table - String[] headerArray = headers.split(","); // Assuming headers are comma-separated - Table table = new Table(headerArray.length); - - // Add header cells - for (String header : headerArray) { - table.addCell(new Cell().add(new Paragraph(header.trim())).setFontSize(8f)); // Wrap the header in a Paragraph - } - - // Add backup data - if (backups != null && !backups.isEmpty()) { - for (ConfigurationBackup backup : backups) { - String[] data = backup.toCsvString().split(","); // Assuming backup data is comma-separated - for (String value : data) { - // new line every 25 characters - for (int i = 0; i < value.length(); i++) { - if (i % 25 == 0) { - value = value.substring(0, i) + "\n" + value.substring(i); - } - } - table.addCell(new Cell().add(new Paragraph(value.trim())).setFontSize(5f)); // Wrap the value in a Paragraph - } - } - } - - // Add table to document - document.add(table); - - // Close document - document.close(); - - // Notify success - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.SUCCESSFULLY_EXPORTED_TO_PDF_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.SUCCESS_GENERIC_TITLE), JOptionPane.INFORMATION_MESSAGE); - - } catch (IOException ex) { - logger.error("Error exporting backups to PDF: " + ex.getMessage(), ex); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_EXPORTING_TO_PDF) + ex.getMessage(), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); - } finally { - logger.info("Exporting backups to PDF finished"); - } - } - - public static void exportAsCSV(ArrayList<ConfigurationBackup> backups, String header) { + public static void exportAsCSV(JFrame component, List<ConfigurationBackup> backups, String header) { logger.info("Exporting backups to CSV"); String path = BackupOperations.pathSearchWithFileChooser(false); @@ -125,7 +33,7 @@ public static void exportAsCSV(ArrayList<ConfigurationBackup> backups, String he return; } - String filename = JOptionPane.showInputDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.CSV_NAME_MESSAGE_INPUT)); + String filename = JOptionPane.showInputDialog(null, Translations.get(TKey.CSV_NAME_MESSAGE_INPUT)); if (filename == null || filename.isEmpty()) { logger.info("Exporting backups to CSV cancelled"); return; @@ -133,7 +41,7 @@ public static void exportAsCSV(ArrayList<ConfigurationBackup> backups, String he // Validate filename if (!filename.matches("[a-zA-Z0-9-_ ]+")) { - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_INVALID_FILENAME), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + ToastUtils.showError(component, Translations.get(TKey.TOAST_CSV_EXPORT_INVALID_FILENAME)); logger.info("Exporting backups to CSV cancelled due to invalid file name"); return; } @@ -144,7 +52,7 @@ public static void exportAsCSV(ArrayList<ConfigurationBackup> backups, String he // Check if the file exists File file = new File(fullPath); if (file.exists()) { - int overwrite = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.DUPLICATED_FILE_NAME_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION); + int overwrite = JOptionPane.showConfirmDialog(null, Translations.get(TKey.DUPLICATED_FILE_NAME_MESSAGE), Translations.get(TKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION); if (overwrite != JOptionPane.YES_OPTION) { logger.info("Exporting backups to CSV cancelled by user (file exists)"); return; @@ -164,10 +72,10 @@ public static void exportAsCSV(ArrayList<ConfigurationBackup> backups, String he } } - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.SUCCESSFULLY_EXPORTED_TO_CSV_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.SUCCESS_GENERIC_TITLE), JOptionPane.INFORMATION_MESSAGE); + ToastUtils.showSuccess(component, Translations.get(TKey.TOAST_CSV_EXPORT)); } catch (IOException ex) { logger.error("Error exporting backups to CSV: " + ex.getMessage(), ex); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_FOR_EXPORTING_TO_CSV) + ex.getMessage(), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + ToastUtils.showError(component, Translations.get(TKey.TOAST_CSV_EXPORT_ERROR) + ": " + ex.getMessage()); } finally { logger.info("Exporting backups to CSV finished"); } diff --git a/src/main/java/backupmanager/Managers/LanguageManager.java b/src/main/java/backupmanager/Managers/LanguageManager.java new file mode 100644 index 00000000..e082681a --- /dev/null +++ b/src/main/java/backupmanager/Managers/LanguageManager.java @@ -0,0 +1,80 @@ +package backupmanager.Managers; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.SwingUtilities; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import backupmanager.Enums.ConfigKey; +import backupmanager.Enums.LanguagesEnum; +import backupmanager.Enums.Translations; +import backupmanager.interfaces.ITranslatable; +import backupmanager.Utils.AppPreferences; + +// Observer class -> every time the language is changed, it notify all the components registered +public class LanguageManager { + private static final Logger logger = LoggerFactory.getLogger(LanguageManager.class); + + private static final List<WeakReference<ITranslatable>> listeners = new ArrayList<>(); + + public static void setLanguage(String language) { + var lang = getLanguageByLanguageName(language); + setLanguage(lang); + } + + public static void setLanguage(LanguagesEnum language) { + AppPreferences.setLanguage(language.getFileName()); + logger.info("Language setted to: {}", language.getLanguageName()); + loadPreferredLanguage(); + notifyLanguageChanged(); + } + + public static void loadPreferredLanguage() { + try { + Translations.loadTranslations(ConfigKey.LANGUAGES_DIRECTORY_STRING.getValue() + LanguageManager.getLanguage().getFileName()); + } catch (IOException ex) { + logger.error("An error occurred during loading preferences: {}", ex.getMessage(), ex); + } + } + + public static LanguagesEnum getLanguage() { + return getLanguageByFileName(AppPreferences.getLanguage()); + } + + public static void register(ITranslatable t) { + listeners.add(new WeakReference<>(t)); + } + + public static void notifyLanguageChanged() { + SwingUtilities.invokeLater(() -> { + listeners.removeIf(ref -> ref.get() == null); + for (WeakReference<ITranslatable> ref : listeners) { + ITranslatable t = ref.get(); + if (t != null) { + t.setTranslations(); + } + } + }); + } + + private static LanguagesEnum getLanguageByFileName(String filename) { + for (LanguagesEnum lang : LanguagesEnum.values()) { + if (lang.getFileName().equalsIgnoreCase(filename)) + return lang; + } + throw new IllegalArgumentException("Impossible fetch language with filename: " + filename); + } + + private static LanguagesEnum getLanguageByLanguageName(String language) { + for (LanguagesEnum lang : LanguagesEnum.values()) { + if (lang.getLanguageName().equalsIgnoreCase(language)) + return lang; + } + throw new IllegalArgumentException("Impossible fetch language with name: " + language); + } +} diff --git a/src/main/java/backupmanager/Managers/ThemeManager.java b/src/main/java/backupmanager/Managers/ThemeManager.java deleted file mode 100644 index b0ef7269..00000000 --- a/src/main/java/backupmanager/Managers/ThemeManager.java +++ /dev/null @@ -1,86 +0,0 @@ -package backupmanager.Managers; - -import java.awt.Component; -import java.awt.Dialog; -import java.awt.Frame; -import java.util.Arrays; - -import javax.swing.JPopupMenu; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.formdev.flatlaf.FlatDarculaLaf; -import com.formdev.flatlaf.FlatIntelliJLaf; -import com.formdev.flatlaf.intellijthemes.FlatArcDarkOrangeIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatArcOrangeIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatCarbonIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatCyanLightIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatHighContrastIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatNordIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatSolarizedDarkIJTheme; -import com.formdev.flatlaf.intellijthemes.FlatSolarizedLightIJTheme; - -import backupmanager.Entities.Confingurations; - -// https://www.formdev.com/flatlaf/#demo -// https://www.formdev.com/flatlaf/themes/ -// https://github.com/JFormDesigner/FlatLaf/tree/main/flatlaf-intellij-themes - -public class ThemeManager { - private static final Logger logger = LoggerFactory.getLogger(ThemeManager.class); - - public static void updateThemeFrame(Frame frame) { - updateTheme(); - repaint(frame); - } - - public static void refreshPopup(JPopupMenu popup) { - repaint(popup); - } - - public static void updateThemeDialog(Dialog dialog) { - updateTheme(); - repaint(dialog); - } - - private static void repaint(Object objectToRepaint) { - if (objectToRepaint == null) - throw new NullPointerException("objectToRepaint cannot be null"); - - 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() { - try { - String selectedTheme = Confingurations.getTheme().getThemeName(); - - switch (selectedTheme.toLowerCase()) { - case "light" -> UIManager.setLookAndFeel(new FlatIntelliJLaf()); - case "dark" -> UIManager.setLookAndFeel(new FlatDarculaLaf()); - case "carbon" -> UIManager.setLookAndFeel(new FlatCarbonIJTheme()); - case "arc - orange" -> UIManager.setLookAndFeel(new FlatArcOrangeIJTheme()); - case "arc dark - orange" -> UIManager.setLookAndFeel(new FlatArcDarkOrangeIJTheme()); - case "cyan light" -> UIManager.setLookAndFeel(new FlatCyanLightIJTheme()); - case "nord" -> UIManager.setLookAndFeel(new FlatNordIJTheme()); - case "high contrast" -> UIManager.setLookAndFeel(new FlatHighContrastIJTheme()); - case "solarized dark" -> UIManager.setLookAndFeel(new FlatSolarizedDarkIJTheme()); - case "solarized light" -> UIManager.setLookAndFeel(new FlatSolarizedLightIJTheme()); - default -> UIManager.setLookAndFeel(new FlatIntelliJLaf()); // If no match, apply the default theme - } - } catch (UnsupportedLookAndFeelException ex) { - logger.error("Error setting LookAndFeel: " + ex.getMessage(), ex); - ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); - } - } -} \ No newline at end of file diff --git a/src/main/java/backupmanager/Managers/WebsiteManager.java b/src/main/java/backupmanager/Managers/WebsiteManager.java index b00235d9..35899ad0 100644 --- a/src/main/java/backupmanager/Managers/WebsiteManager.java +++ b/src/main/java/backupmanager/Managers/WebsiteManager.java @@ -5,20 +5,22 @@ import java.net.URI; import java.net.URISyntaxException; -import javax.swing.JOptionPane; +import javax.swing.JFrame; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Utils.ToastUtils; public class WebsiteManager { private static final Logger logger = LoggerFactory.getLogger(WebsiteManager.class); - public static void openWebSite(String reportUrl) { + public static void openWebSite(JFrame parent, String reportUrl) { try { + logger.info("Opening website: {}", reportUrl); if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.BROWSE)) { @@ -27,11 +29,12 @@ public static void openWebSite(String reportUrl) { } } catch (IOException | URISyntaxException e) { logger.error("Failed to open the web page: " + e.getMessage(), e); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_OPENING_WEBSITE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + ToastUtils.showError(parent, Translations.get(TKey.TOAST_OPENING_WEBSITE_ERROR)); } } - public static void sendEmail() { + public static void sendEmail(JFrame parent) { + logger.info("Attempting to open default mail client (support request)"); if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); @@ -44,15 +47,15 @@ public static void sendEmail() { desktop.mail(uri); } catch (IOException | URISyntaxException ex) { logger.error("Failed to send email: " + ex.getMessage(), ex); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_UNABLE_TO_SEND_EMAIL), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + ToastUtils.showError(parent, Translations.get(TKey.TOAST_UNABLE_TO_SEND_EMAIL)); } } else { logger.warn("Mail action is unsupported in your system's desktop environment."); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_NOT_SUPPORTED_EMAIL), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + ToastUtils.showError(parent, Translations.get(TKey.TOAST_NOT_SUPPORTED_EMAIL)); } } else { logger.warn("Desktop integration is unsupported on this system."); - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_MESSAGE_NOT_SUPPORTED_EMAIL_GENERIC), TranslationCategory.DIALOGS.getTranslation(TranslationKey.ERROR_GENERIC_TITLE), JOptionPane.ERROR_MESSAGE); + ToastUtils.showError(parent, Translations.get(TKey.TOAST_NOT_SUPPORTED_EMAIL_GENERIC)); } } diff --git a/src/main/java/backupmanager/Services/BackgroundService.java b/src/main/java/backupmanager/Services/BackgroundService.java index 94c8bdfe..a5f3c1cd 100644 --- a/src/main/java/backupmanager/Services/BackgroundService.java +++ b/src/main/java/backupmanager/Services/BackgroundService.java @@ -14,15 +14,16 @@ import org.slf4j.LoggerFactory; import backupmanager.BackupOperations; -import backupmanager.Controllers.TrayController; +import backupmanager.Entities.BackupExecutionContext; import backupmanager.Entities.BackupRequest; +import backupmanager.Entities.BackupUIContext; import backupmanager.Entities.ConfigurationBackup; import backupmanager.Entities.ZippingContext; import backupmanager.Enums.BackupTriggerType; -import backupmanager.Enums.ConfigKey; -import backupmanager.Json.JSONConfigReader; +import backupmanager.Json.JsonConfig; import backupmanager.database.Repositories.BackupConfigurationRepository; import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.gui.Controllers.TrayController; public class BackgroundService { private static final Logger logger = LoggerFactory.getLogger(BackgroundService.class); @@ -30,7 +31,7 @@ public class BackgroundService { private ScheduledExecutorService scheduler; private TrayController trayIcon; - private final JSONConfigReader jsonConfig = new JSONConfigReader(ConfigKey.CONFIG_FILE_STRING.getValue(), ConfigKey.CONFIG_DIRECTORY_STRING.getValue()); + private final JsonConfig jsonConfig = JsonConfig.getInstance(); private final AtomicBoolean isBackingUp = new AtomicBoolean(false); public void start(TrayController trayIcon) throws IOException { @@ -116,8 +117,11 @@ private void executeBackups(List<ConfigurationBackup> backups) { javax.swing.SwingUtilities.invokeLater(() -> { try { for (ConfigurationBackup backup : backups) { - ZippingContext context = ZippingContext.create(backup, trayIcon.getTrayIcon(), null, null, null, null); - BackupOperations.singleBackup(context, BackupTriggerType.SCHEDULER); + ZippingContext context = new ZippingContext( + BackupExecutionContext.create(backup), + new BackupUIContext(trayIcon.getTrayIcon(), null, null, null, null) + ); + BackupOperations.requestSingleBackup(context, BackupTriggerType.SCHEDULER); } } finally { logger.info("All backups completed. Resetting isBackingUp flag."); diff --git a/src/main/java/backupmanager/Services/BackupAnalyticsService.java b/src/main/java/backupmanager/Services/BackupAnalyticsService.java new file mode 100644 index 00000000..1a18576e --- /dev/null +++ b/src/main/java/backupmanager/Services/BackupAnalyticsService.java @@ -0,0 +1,141 @@ +package backupmanager.Services; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.jfree.data.category.CategoryDataset; +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.time.Day; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; +import org.jfree.data.xy.XYDataset; + +import backupmanager.Entities.BackupAnalyticsSnapshot; +import backupmanager.Entities.BackupRequest; +import backupmanager.Enums.BackupStatus; + +public class BackupAnalyticsService { + + public static BackupAnalyticsSnapshot buildSnapshot(List<BackupRequest> requests) { + + if (requests == null || requests.isEmpty()) { + return BackupAnalyticsSnapshot.emptyDataset(); + } + + long total = requests.size(); + + long successCount = requests.stream() + .filter(r -> r.status() == BackupStatus.FINISHED) + .count(); + + long failedCount = requests.stream() + .filter(r -> r.status() == BackupStatus.TERMINATED) + .count(); + + double successRate = total == 0 ? 0 : successCount * 100.0 / total; + + double avgDuration = requests.stream() + .filter(r -> r.durationMs() != null) + .mapToLong(BackupRequest::durationMs) + .average() + .orElse(0); + + double avgCompressionRate = computeCompressionRate(requests); + + long diskUsage = requests.stream() + .mapToLong(r -> + r.zippedTargetSize() != null ? + r.zippedTargetSize() : + 0) + .sum(); + + Map<LocalDate, Double> durationTrend = + requests.stream() + .filter(r -> r.durationMs() != null) + .collect(Collectors.groupingBy( + r -> r.startedDate().toLocalDate(), + Collectors.averagingDouble( + r -> r.durationMs() / 60000.0 + ))); + + return new BackupAnalyticsSnapshot(total, successCount, failedCount, successRate, avgDuration, avgCompressionRate, diskUsage, durationTrend); + } + + public static CategoryDataset buildRequestsPerMonthDataset(List<BackupRequest> requests, String title) { + + DefaultCategoryDataset dataset = new DefaultCategoryDataset(); + + LocalDate now = LocalDate.now(); + LocalDate oneYearAgo = now.minusMonths(11).withDayOfMonth(1); + + Map<String, Long> map = requests.stream() + .filter(r -> !r.startedDate().toLocalDate().isBefore(oneYearAgo)) + .collect(Collectors.groupingBy( + r -> { + var d = r.startedDate(); + return d.getYear() + "-" + String.format("%02d", d.getMonthValue()); + }, + Collectors.counting() + )); + + List<String> last12Months = IntStream.rangeClosed(0, 11) + .mapToObj(i -> { + LocalDate month = oneYearAgo.plusMonths(i); + return month.getYear() + "-" + String.format("%02d", month.getMonthValue()); + }) + .toList(); + + for (String month : last12Months) { + dataset.addValue(map.getOrDefault(month, 0L), title, month); + } + + return dataset; + } + + public static XYDataset buildDurationTrendDataset(Map<LocalDate, Double> trendMap, String title) { + + TimeSeries series = new TimeSeries(title); + + LocalDate today = LocalDate.now(); + LocalDate thirtyDaysAgo = today.minusDays(29); + + trendMap.entrySet().stream() + .filter(entry -> !entry.getKey().isBefore(thirtyDaysAgo)) + .sorted(Map.Entry.comparingByKey()) + .forEach(entry -> { + LocalDate date = entry.getKey(); + Double value = entry.getValue(); + + series.add( + new Day(date.getDayOfMonth(), date.getMonthValue(), date.getYear()), + value + ); + }); + + return new TimeSeriesCollection(series); + } + + public static double computeCompressionRate(List<BackupRequest> requests) { + + long totalUnzipped = requests.stream() + .mapToLong(BackupRequest::unzippedTargetSize) + .sum(); + + long totalZipped = requests.stream() + .filter(r -> r.zippedTargetSize() != null) + .mapToLong(BackupRequest::zippedTargetSize) + .sum(); + + if (totalUnzipped == 0) + return 0; + + return (double) totalZipped / totalUnzipped; + } + + public static double convertAvgDurationinMinutes(BackupAnalyticsSnapshot snapshot) { + return snapshot.avgDurationMs() / 60000.0; + } +} diff --git a/src/main/java/backupmanager/Services/BackupObserver.java b/src/main/java/backupmanager/Services/BackupObserver.java index 72caea3d..d8fcf284 100644 --- a/src/main/java/backupmanager/Services/BackupObserver.java +++ b/src/main/java/backupmanager/Services/BackupObserver.java @@ -1,8 +1,11 @@ package backupmanager.Services; -import java.time.format.DateTimeFormatter; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -14,25 +17,31 @@ import backupmanager.Entities.BackupRequest; import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Table.TableDataManager; import backupmanager.database.Repositories.BackupConfigurationRepository; import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.gui.Table.BackupTableDataService; /* * I need a task that constantly checks if there are something running and i can't use a simple method calls instead because - * if a backup starts caused by the BackugroundService and we open the GUI, thre are 2 different instance of this program, + * if a backup starts caused by the BackugroundService and we open the GUI, thre are 2 different instance of this program, * so we need something like an observer that constantly checks if there are some backups in progress. */ public class BackupObserver { private static final Logger logger = LoggerFactory.getLogger(BackupObserver.class); private final ScheduledExecutorService scheduler; - private final DateTimeFormatter formatter; + private final BackupTableDataService tableService; private final long millisecondsToWait; - public BackupObserver(DateTimeFormatter formatter, int millisecondsToWait) { + // track last-seen timestamps for backups that were running + private final ConcurrentMap<Integer, Long> lastSeenRunning = new ConcurrentHashMap<>(); + + // grace period to wait before removing progress indicator (milliseconds) + private final long graceMillis = 3000L; + + public BackupObserver(BackupTableDataService tableService, int millisecondsToWait) { + this.tableService = tableService; this.millisecondsToWait = millisecondsToWait; - this.formatter = formatter; this.scheduler = Executors.newSingleThreadScheduledExecutor(); // create single thread } @@ -45,6 +54,11 @@ public void start() { Map<Integer, ConfigurationBackup> configs = BackupConfigurationRepository.getBackupMap(); + // Collect running configuration ids + Set<Integer> runningConfigIds = new HashSet<>(); + for (BackupRequest r : running) runningConfigIds.add(r.backupConfigurationId()); + + // Update progress for running backups for (BackupRequest request : running) { ConfigurationBackup config = configs.get(request.backupConfigurationId()); @@ -57,13 +71,41 @@ public void start() { BackupRequest updatedRequest = BackupRequestRepository.getBackupRequestById(request.backupRequestId()); if (updatedRequest != null && updatedRequest.progress() < 99) { - TableDataManager.updateProgressBarPercentage(config, updatedRequest.progress(), formatter); + tableService.updateProgress(config, updatedRequest.progress()); } else { - TableDataManager.removeProgressInTheTableAndRestoreAsDefault(config, formatter); + tableService.removeProgress(config); } }); } + long now = System.currentTimeMillis(); + + // Update last seen timestamps for running backups + for (BackupRequest r : running) { + lastSeenRunning.put(r.backupConfigurationId(), now); + } + + // Cleanup any progress indicators for backups that are not currently running, + // but only if we saw them running before and the grace period has elapsed. + for (ConfigurationBackup config : configs.values()) { + int id = config.getId(); + if (!runningConfigIds.contains(id)) { + Long lastSeen = lastSeenRunning.get(id); + if (lastSeen != null) { + if (now - lastSeen >= graceMillis) { + lastSeenRunning.remove(id); + SwingUtilities.invokeLater(() -> { + try { + tableService.removeProgress(config); + } catch (Exception e) { + logger.debug("Error while cleaning obsolete progress for {}: {}", config.getName(), e.getMessage()); + } + }); + } + } + } + } + } catch (Exception ex) { logger.error("Observer error", ex); } diff --git a/src/main/java/backupmanager/Services/BackupService.java b/src/main/java/backupmanager/Services/BackupService.java index 4a446caa..4419ffb1 100644 --- a/src/main/java/backupmanager/Services/BackupService.java +++ b/src/main/java/backupmanager/Services/BackupService.java @@ -1,10 +1,11 @@ package backupmanager.Services; +import java.time.LocalDateTime; import java.util.List; import backupmanager.Entities.ConfigurationBackup; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; import static backupmanager.Helpers.BackupHelper.formatter; import backupmanager.database.Repositories.BackupConfigurationRepository; @@ -13,50 +14,60 @@ public List<ConfigurationBackup> getAllBackups() { return BackupConfigurationRepository.getBackupList(); } - public boolean isRunning(String name) { - return RunningBackupService.getRunningBackupByName(name).isPresent(); + public void updateBackup(ConfigurationBackup backup) { + BackupConfigurationRepository.updateBackup(backup); } - public void deleteBackups(List<String> names) { - names.forEach(name -> { - ConfigurationBackup backup = BackupConfigurationRepository.getBackupByName(name); - if (backup != null) { - BackupConfigurationRepository.deleteBackup(backup.getId()); - } - }); + public String buildDetails(ConfigurationBackup backup) { + String backupNameStr = Translations.get(TKey.BACKUP_NAME_DETAIL); + String initialPathStr = Translations.get(TKey.INITIAL_PATH_DETAIL); + String destinationPathStr = Translations.get(TKey.DESTINATION_PATH_DETAIL); + String lastBackupStr = Translations.get(TKey.LAST_BACKUP_DETAIL); + String nextBackupStr = Translations.get(TKey.NEXT_BACKUP_DATE_DETAIL); + String timeIntervalBackupStr = Translations.get(TKey.TIME_INTERVAL_DETAIL); + String creationDateStr = Translations.get(TKey.CREATION_DATE_DETAIL); + String lastUpdateDateStr = Translations.get(TKey.LAST_UPDATE_DATE_DETAIL); + String backupCountStr = Translations.get(TKey.BACKUP_COUNT_DETAIL); + String notesStr = Translations.get(TKey.NOTES_DETAIL); + String maxBackupsToKeepStr = Translations.get(TKey.MAX_BACKUPS_TO_KEEP_DETAIL); + + return """ + <html> + <div style='font-family:sans-serif; font-size:10px; padding:2px'> + + <b>%s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + <b> %s:</b> %s. + + </div> + </html> + """.formatted( + backupNameStr, backup.getName(), + initialPathStr, backup.getTargetPath(), + destinationPathStr, backup.getDestinationPath(), + lastBackupStr, formatDate(backup.getLastBackupDate()), + nextBackupStr, formatDate(backup.getNextBackupDate()), + timeIntervalBackupStr, optionalString(backup.getTimeIntervalBackup()), + creationDateStr, formatDate(backup.getCreationDate()), + lastUpdateDateStr, formatDate(backup.getLastUpdateDate()), + backupCountStr, backup.getCount(), + maxBackupsToKeepStr, backup.getMaxToKeep(), + notesStr, backup.getNotes() + ); } - public String getBackupDetails(String name) { - ConfigurationBackup backup = BackupConfigurationRepository.getBackupByName(name); - return buildDetails(backup); + private String formatDate(LocalDateTime date) { + return date != null ? date.format(formatter) : "_"; } - private String buildDetails(ConfigurationBackup backup) { - String backupNameStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.BACKUP_NAME_DETAIL); - String initialPathStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.INITIAL_PATH_DETAIL); - String destinationPathStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.DESTINATION_PATH_DETAIL); - String lastBackupStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.LAST_BACKUP_DETAIL); - String nextBackupStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.NEXT_BACKUP_DATE_DETAIL); - String timeIntervalBackupStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.TIME_INTERVAL_DETAIL); - String creationDateStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.CREATION_DATE_DETAIL); - String lastUpdateDateStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.LAST_UPDATE_DATE_DETAIL); - String backupCountStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.BACKUP_COUNT_DETAIL); - String notesStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.NOTES_DETAIL); - String maxBackupsToKeepStr = TranslationCategory.BACKUP_LIST.getTranslation(TranslationKey.MAX_BACKUPS_TO_KEEP_DETAIL); - - return ( - "<html><b>" + backupNameStr + ":</b> " + backup.getName() + ", " + - "<b>" + initialPathStr + ":</b> " + backup.getTargetPath() + ", " + - "<b>" + destinationPathStr + ":</b> " + backup.getDestinationPath() + ", " + - "<b>" + lastBackupStr + ":</b> " + (backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "") + ", " + - "<b>" + nextBackupStr + ":</b> " + (backup.getNextBackupDate() != null ? backup.getNextBackupDate().format(formatter) : "_") + ", " + - "<b>" + timeIntervalBackupStr + ":</b> " + (backup.getTimeIntervalBackup() != null ? backup.getTimeIntervalBackup().toString() : "_") + ", " + - "<b>" + creationDateStr + ":</b> " + (backup.getCreationDate() != null ? backup.getCreationDate().format(formatter) : "_") + ", " + - "<b>" + lastUpdateDateStr + ":</b> " + (backup.getLastUpdateDate() != null ? backup.getLastUpdateDate().format(formatter) : "_") + ", " + - "<b>" + backupCountStr + ":</b> " + (backup.getCount()) + ", " + - "<b>" + maxBackupsToKeepStr + ":</b> " + (backup.getMaxToKeep()) + ", " + - "<b>" + notesStr + ":</b> " + (backup.getNotes()) + - "</html>" - ); + private String optionalString(Object value) { + return value != null ? value.toString() : "_"; } } diff --git a/src/main/java/backupmanager/Services/LoginService.java b/src/main/java/backupmanager/Services/LoginService.java new file mode 100644 index 00000000..4d1aa909 --- /dev/null +++ b/src/main/java/backupmanager/Services/LoginService.java @@ -0,0 +1,63 @@ +package backupmanager.Services; + +import java.util.Locale; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import backupmanager.Email.EmailSender; +import backupmanager.Entities.User; +import backupmanager.Enums.LanguagesEnum; +import backupmanager.Managers.LanguageManager; +import backupmanager.database.Repositories.UserRepository; + +public class LoginService { + + private static final Logger logger = LoggerFactory.getLogger(LoginService.class); + + public boolean isFirstAccess() { + logger.debug("Checking for first access"); + User user = UserRepository.getLastUser(); + + if (user == null) { + setLanguageBasedOnPcLanguage(); + return true; + } else { + logger.info("Current user: " + user.toString()); + return false; + } + } + + public void createNewUser(User user) { + if (user == null) throw new IllegalArgumentException("User cannot be null"); + UserRepository.insertUser(user); + } + + public void createUserAndSendEmail(User user) { + createNewUser(user); + sendRegistrationEmail(user); + } + + private void setLanguageBasedOnPcLanguage() { + Locale defaultLocale = Locale.getDefault(); + String language = defaultLocale.getLanguage(); + + logger.info("Setting default language to: " + language); + + LanguagesEnum languageValue; + switch (language) { + case "en" -> languageValue = LanguagesEnum.ENG; + case "it" -> languageValue = LanguagesEnum.ITA; + case "es" -> languageValue = LanguagesEnum.ESP; + case "de" -> languageValue = LanguagesEnum.DEU; + case "fr" -> languageValue = LanguagesEnum.FRA; + default -> languageValue = LanguagesEnum.getDefault(); + } + LanguageManager.setLanguage(languageValue); + } + + private void sendRegistrationEmail(User user) { + EmailSender.sendUserCreationEmail(user); + EmailSender.sendConfirmEmailToUser(user); + } +} diff --git a/src/main/java/backupmanager/Services/PreferenceService.java b/src/main/java/backupmanager/Services/PreferenceService.java deleted file mode 100644 index de92149d..00000000 --- a/src/main/java/backupmanager/Services/PreferenceService.java +++ /dev/null @@ -1,16 +0,0 @@ -package backupmanager.Services; - - -import java.io.IOException; - -import backupmanager.Entities.Confingurations; -import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum; - -public class PreferenceService { - public void updatePreferences(String language, String theme) throws IOException { - Confingurations.setLanguageByLanguageName(language); - Confingurations.setTheme(theme); - TranslationLoaderEnum.loadTranslations(ConfigKey.LANGUAGES_DIRECTORY_STRING.getValue() + Confingurations.getLanguage().getFileName()); - } -} diff --git a/src/main/java/backupmanager/Services/RunningBackupService.java b/src/main/java/backupmanager/Services/RunningBackupService.java index 96872fdd..19fa081c 100644 --- a/src/main/java/backupmanager/Services/RunningBackupService.java +++ b/src/main/java/backupmanager/Services/RunningBackupService.java @@ -9,11 +9,12 @@ import backupmanager.Enums.BackupStatus; import backupmanager.Helpers.BackupHelper; import backupmanager.Helpers.SqlHelper; -import backupmanager.Utils.FolderUtils; import backupmanager.database.Repositories.BackupConfigurationRepository; import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.Utils.FolderUtils; public class RunningBackupService { + public static Optional<BackupRequest> getRunningBackupByName(String backupName) { ConfigurationBackup config = BackupConfigurationRepository.getBackupByName(backupName); if (config == null) return Optional.empty(); @@ -32,7 +33,7 @@ public static void updateBackupZippedFolderSizeById(int requestId, String pathFo public static void updateBackupStatusAfterForceTerminationByBackupConfigurationId(int backupConfigurationId) { BackupRequest request = BackupRequestRepository.getLastBackupInProgressByConfigurationId(backupConfigurationId); - BackupHelper.forceBackupTermination(request.backupRequestId()); + BackupHelper.forceBackupTermination(request); } public static void updateBackupStatusAfterCompletitionByBackupConfigurationId(int backupConfigurationId) { diff --git a/src/main/java/backupmanager/Services/ZippingThread.java b/src/main/java/backupmanager/Services/ZippingThread.java index 743d800f..7472586c 100644 --- a/src/main/java/backupmanager/Services/ZippingThread.java +++ b/src/main/java/backupmanager/Services/ZippingThread.java @@ -66,7 +66,7 @@ public static void zipDirectory(File sourceFile, File outputFile, ZippingContext private static void handleError(String message, ErrorType errorType, ZippingContext context) { logger.error(message); - BackupOperations.setError(errorType, context.trayIcon(), null); + BackupOperations.setError(errorType, context.ui().trayIcon(), null); BackupOperations.reEnableButtonsAndTable(context); } diff --git a/src/main/java/backupmanager/Table/StripedRowRenderer.java b/src/main/java/backupmanager/Table/StripedRowRenderer.java deleted file mode 100644 index 1bfea4ea..00000000 --- a/src/main/java/backupmanager/Table/StripedRowRenderer.java +++ /dev/null @@ -1,34 +0,0 @@ -package backupmanager.Table; - -import java.awt.Color; -import java.awt.Component; - -import javax.swing.JTable; -import javax.swing.table.DefaultTableCellRenderer; - -public class StripedRowRenderer extends DefaultTableCellRenderer { - private final Color evenRowColor = new Color(223, 222, 243); - private final Color oddRowColor = Color.WHITE; - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - // Apply striped row colors - if (row % 2 == 0) { - c.setBackground(evenRowColor); - } else { - c.setBackground(oddRowColor); - } - - // Handle selection - if (isSelected) { - c.setBackground(table.getSelectionBackground()); - c.setForeground(table.getSelectionForeground()); - } else { - c.setForeground(Color.BLACK); - } - - return c; - } -} diff --git a/src/main/java/backupmanager/Table/TableDataManager.java b/src/main/java/backupmanager/Table/TableDataManager.java deleted file mode 100644 index 235b6b89..00000000 --- a/src/main/java/backupmanager/Table/TableDataManager.java +++ /dev/null @@ -1,106 +0,0 @@ -package backupmanager.Table; - -import java.time.format.DateTimeFormatter; -import java.util.List; - -import javax.swing.SwingUtilities; -import javax.swing.table.TableColumnModel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import backupmanager.Entities.ConfigurationBackup; -import backupmanager.GUI.BackupManagerGUI; - -public class TableDataManager { - - private static final Logger logger = LoggerFactory.getLogger(TableDataManager.class); - - public static void removeProgressInTheTableAndRestoreAsDefault(ConfigurationBackup backup, DateTimeFormatter formatter) { - if (backup == null) throw new IllegalArgumentException("Backup cannot be null"); - if (formatter == null) throw new IllegalArgumentException("Formatter cannot be null"); - - if (BackupManagerGUI.backupTable == null) { - return; - } - - // remove the progress bar renderer - BackupManagerGUI.backupTable.getColumnModel().getColumn(3).setCellRenderer(new StripedRowRenderer()); - - // Set last backup value in the table - BackupManagerGUI.backupTable.getModel().setValueAt( - backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "", - TableDataManager.findBackupRowIndex(backup, BackupManagerGUI.backupTable), 3); - - BackupManagerGUI.backupTable.repaint(); // Repaints the whole table - BackupManagerGUI.backupTable.revalidate(); // Revalidates the table layout - } - - public static void updateProgressBarPercentage(ConfigurationBackup backup, int value, DateTimeFormatter formatter) { - if (backup == null) throw new IllegalArgumentException("Backup cannot be null"); - if (value < 0 || value > 100) throw new IllegalArgumentException("Value must be between 0 and 100"); - if (formatter == null) throw new IllegalArgumentException("Formatter cannot be null"); - - if (BackupManagerGUI.backupTable == null) { - return; - } - - SwingUtilities.invokeLater(() -> { - // Locate the row index of the backup in the table - int rowIndex = TableDataManager.findBackupRowIndex(backup, BackupManagerGUI.backupTable); - if (rowIndex != -1) { - TableColumnModel columnModel = BackupManagerGUI.backupTable.getColumnModel(); - int targetColumnIndex = 3; - - columnModel.getColumn(targetColumnIndex).setCellRenderer(new ProgressBarRenderer()); - - // Restore the original renderer after completion - if (value == 100) { - logger.debug("Restore the original renderer after completion"); - BackupManagerGUI.backupTable.getModel().setValueAt( - backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "", - rowIndex, - targetColumnIndex - ); - } else { - // Update the value of the progress in the table - BackupManagerGUI.backupTable.getModel().setValueAt(value, rowIndex, targetColumnIndex); - } - - BackupManagerGUI.backupTable.repaint(); - } - }); - } - - public static void updateTableWithNewBackupList(List<ConfigurationBackup> updatedBackups, DateTimeFormatter formatter) { - logger.debug("updating backup list"); - - SwingUtilities.invokeLater(() -> { - BackupManagerGUI.model.setRowCount(0); - - for (ConfigurationBackup backup : updatedBackups) { - BackupManagerGUI.model.addRow(new Object[]{ - backup.getName(), - backup.getTargetPath(), - backup.getDestinationPath(), - backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "", - backup.isAutomatic(), - backup.getNextBackupDate() != null ? backup.getNextBackupDate().format(formatter) : "", - backup.getTimeIntervalBackup() != null ? backup.getTimeIntervalBackup().toString() : "" - }); - } - }); - } - - private static int findBackupRowIndex(ConfigurationBackup backup, BackupTable table) { - if (backup == null) throw new IllegalArgumentException("Backup cannot be null"); - if (table == null) throw new IllegalArgumentException("Table cannot be null"); - - for (int i = 0; i < table.getRowCount(); i++) { - if (table.getValueAt(i, 0).equals(backup.getName())) { // first column holds unique backup names - return i; - } - } - return -1; - } -} diff --git a/src/main/java/backupmanager/Utils/AppPreferences.java b/src/main/java/backupmanager/Utils/AppPreferences.java new file mode 100644 index 00000000..632b54e1 --- /dev/null +++ b/src/main/java/backupmanager/Utils/AppPreferences.java @@ -0,0 +1,142 @@ +package backupmanager.Utils; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.prefs.Preferences; + +import javax.swing.UIManager; + +import com.formdev.flatlaf.FlatDarculaLaf; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.IntelliJTheme; +import com.formdev.flatlaf.util.LoggingFacade; + +import backupmanager.Enums.LanguagesEnum; +import backupmanager.gui.themes.PanelThemes; + +public class AppPreferences { + + public static final String PREFERENCES_ROOT_PATH = "/BackupManager"; + public static final String KEY_LAF = "laf"; + public static final String KEY_LAF_THEME = "lafTheme"; + public static final String KEY_ACCENT_COLOR = "accent"; + public static final String KEY_RECENT_SEARCH = "recentSearch"; + public static final String KEY_RECENT_SEARCH_FAVORITE = "recentSearchFavorite"; + public static final String RESOURCE_PREFIX = "res:"; + public static final String THEME_UI_KEY = "__RaVen.flatlaf.demo.theme"; + public static final String KEY_LANGUAGE = "language"; + public static Color accentColor; + private static Preferences state; + + public static Preferences getState() { + return state; + } + + public static void init() { + state = Preferences.userRoot().node(PREFERENCES_ROOT_PATH); + } + + public static void setupLaf() { + // set look and feel + try { + String lafClassName = state.get(KEY_LAF, FlatDarculaLaf.class.getName()); + String rgbAccentColor = state.get(KEY_ACCENT_COLOR, null); + if (rgbAccentColor != null) { + accentColor = new Color(Integer.parseInt(rgbAccentColor), true); + FlatLaf.setSystemColorGetter(name -> name.equals("accent") ? accentColor : null); + } + + if (IntelliJTheme.ThemeLaf.class.getName().equals(lafClassName)) { + String theme = state.get(KEY_LAF_THEME, ""); + if (theme.startsWith(RESOURCE_PREFIX)) { + IntelliJTheme.setup(PanelThemes.class.getResourceAsStream(PanelThemes.THEMES_PACKAGE + theme.substring(RESOURCE_PREFIX.length()))); + } else { + FlatDarculaLaf.setup(); + } + if (!theme.isEmpty()) { + UIManager.getLookAndFeelDefaults().put(THEME_UI_KEY, theme); + } + } else { + UIManager.setLookAndFeel(lafClassName); + } + } catch (Exception e) { + LoggingFacade.INSTANCE.logSevere(null, e); + FlatDarculaLaf.setup(); + } + UIManager.addPropertyChangeListener(e -> { + if (e.getPropertyName().equals("lookAndFeel")) { + state.put(KEY_LAF, UIManager.getLookAndFeel().getClass().getName()); + } + }); + } + + public static String[] getRecentSearch(boolean favorite) { + String stringArr = state.get(favorite ? KEY_RECENT_SEARCH_FAVORITE : KEY_RECENT_SEARCH, null); + if (stringArr == null || stringArr.trim().isEmpty()) return null; + return stringArr.trim().split(","); + } + + public static void addRecentSearch(String value, boolean favorite) { + String[] oldRecent = getRecentSearch(false); + String[] oldFavorite = getRecentSearch(true); + if (favorite) { + if (oldRecent != null) { + // remove from recent search + List<String> list = new ArrayList<>(Arrays.asList(oldRecent)); + list.remove(value); + state.put(KEY_RECENT_SEARCH, String.join(",", list)); + } + if (oldFavorite != null) { + List<String> list = new ArrayList<>(Arrays.asList(oldFavorite)); + list.remove(value); + list.add(0, value); + state.put(KEY_RECENT_SEARCH_FAVORITE, String.join(",", list)); + } else { + state.put(KEY_RECENT_SEARCH_FAVORITE, value); + } + } else { + if (oldFavorite != null) { + List<String> list = new ArrayList<>(Arrays.asList(oldFavorite)); + if (list.contains(value)) { + return; + } + } + if (oldRecent == null) { + state.put(KEY_RECENT_SEARCH, value); + } else { + List<String> list = new ArrayList<>(Arrays.asList(oldRecent)); + list.remove(value); + list.add(0, value); + state.put(KEY_RECENT_SEARCH, String.join(",", list)); + } + } + } + + public static void removeRecentSearch(String value, boolean favorite) { + String[] oldRecent = getRecentSearch(favorite); + if (oldRecent != null) { + List<String> list = new ArrayList<>(Arrays.asList(oldRecent)); + list.remove(value); + state.put(favorite ? KEY_RECENT_SEARCH_FAVORITE : KEY_RECENT_SEARCH, String.join(",", list)); + } + } + + public static void updateAccentColor(Color color) { + if (color != null) { + String rgb = color.getRGB() + ""; + state.put(KEY_ACCENT_COLOR, rgb); + } else { + state.remove(KEY_ACCENT_COLOR); + } + } + + public static String getLanguage() { + return state.get(KEY_LANGUAGE, LanguagesEnum.getDefault().getFileName()); + } + + public static void setLanguage(String lang) { + state.put(KEY_LANGUAGE, lang); + } +} diff --git a/src/main/java/backupmanager/Utils/ModalUtils.java b/src/main/java/backupmanager/Utils/ModalUtils.java new file mode 100644 index 00000000..8649dc41 --- /dev/null +++ b/src/main/java/backupmanager/Utils/ModalUtils.java @@ -0,0 +1,60 @@ +package backupmanager.Utils; + +import java.awt.Component; + +import raven.modal.ModalDialog; +import raven.modal.option.BorderOption; +import raven.modal.option.Location; +import raven.modal.option.Option; + +public class ModalUtils { + + public static void showDefault(Component owner, String title, String message, int options) { + showCustomModal(owner, SimpleMessageModal.Type.DEFAULT, title, message, options); + } + + public static void showInfo(Component owner, String title, String message, int options) { + showCustomModal(owner, SimpleMessageModal.Type.INFO, title, message, options); + } + + public static void showSuccess(Component owner, String title, String message, int options) { + showCustomModal(owner, SimpleMessageModal.Type.SUCCESS, title, message, options); + } + + public static void showWarning(Component owner, String title, String message, int options) { + showCustomModal(owner, SimpleMessageModal.Type.WARNING, title, message, options); + } + + public static void showError(Component owner, String title, String message, int options) { + showCustomModal(owner, SimpleMessageModal.Type.ERROR, title, message, options); + } + + private static void showCustomModal(Component owner, SimpleMessageModal.Type type, String title, String message, int options) { + SimpleMessageModal modal = new SimpleMessageModal(type, message, title, options, null); + ModalDialog.showModal(owner, modal, getSelectedOption()); + } + + private static Option getSelectedOption() { + Option option = ModalDialog.createOption(); + float scale = 0.1f; + Location h = Location.CENTER; + Location v = Location.CENTER; + Option.BackgroundClickType backgroundClickType = Option.BackgroundClickType.BLOCK; + option.setAnimationEnabled(true) + .setCloseOnPressedEscape(true) + .setBackgroundClickType(backgroundClickType) + .setOpacity(0.5f) + .setHeavyWeight(false); + option.getBorderOption() + .setBorderWidth(0) + .setShadow(BorderOption.Shadow.NONE); + option.getLayoutOption().setLocation(h, v) + .setRelativeToOwner(false) + .setMovable(false); + if (scale != 0) { + option.getLayoutOption().setAnimateDistance(0, 0) + .setAnimateScale(scale); + } + return option; + } +} diff --git a/src/main/java/backupmanager/Utils/SimpleMessageModal.java b/src/main/java/backupmanager/Utils/SimpleMessageModal.java new file mode 100644 index 00000000..d972d358 --- /dev/null +++ b/src/main/java/backupmanager/Utils/SimpleMessageModal.java @@ -0,0 +1,173 @@ +package backupmanager.Utils; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; + +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.text.DefaultCaret; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import net.miginfocom.swing.MigLayout; +import raven.modal.Toast; +import raven.modal.component.SimpleModalBorder; +import raven.modal.listener.ModalCallback; +import raven.modal.toast.ToastPanel; + +public class SimpleMessageModal extends SimpleModalBorder { + + private final Type type; + private Component titleComponent; + + public SimpleMessageModal(Type type, String message, String title, int optionType, ModalCallback callback) { + this(type, createMessage(type, message), title, optionType, callback); + } + + public SimpleMessageModal(Type type, Component messageComponent, String title, int optionType, ModalCallback callback) { + super(messageComponent, title, optionType, callback); + this.type = type; + } + + public SimpleMessageModal(Type type, String message, Component titleComponent, int optionType, ModalCallback callback) { + this(type, createMessage(type, message), titleComponent, optionType, callback); + } + + public SimpleMessageModal(Type type, Component messageComponent, Component titleComponent, int optionType, ModalCallback callback) { + super(messageComponent, null, optionType, callback); + this.titleComponent = titleComponent; + this.type = type; + } + + private static Component createMessage(Type type, String message) { + JTextArea text = new JTextArea(message); + text.setWrapStyleWord(true); + text.setEditable(false); + text.setCaret(new DefaultCaret() { + @Override + public void paint(Graphics g) { + } + }); + String gap = type == Type.DEFAULT ? "30" : "62"; + text.putClientProperty(FlatClientProperties.STYLE, "" + + "border:0," + gap + ",10,30;" + + "[light]foreground:lighten($Label.foreground,20%);" + + "[dark]foreground:darken($Label.foreground,20%);"); + return text; + } + + @Override + protected JComponent createTitleComponent(String title) { + if (titleComponent != null && titleComponent instanceof JComponent) { + return (JComponent) titleComponent; + } + if (type == Type.DEFAULT) { + return super.createTitleComponent(title); + } + Icon icon = createIcon(type); + JLabel label = (JLabel) super.createTitleComponent(title); + label.setIconTextGap(10); + label.setIcon(icon); + return label; + } + + @Override + protected JComponent createOptionButton(Option[] optionsType) { + JPanel panel = (JPanel) super.createOptionButton(optionsType); + if (panel == null) return null; + + // modify layout option + if (panel.getLayout() instanceof MigLayout) { + MigLayout layout = (MigLayout) panel.getLayout(); + layout.setColumnConstraints("[]12[]"); + } + return panel; + } + + @Override + protected JButton createButtonOption(Option option) { + JButton button; + if (option.getType() != 0) { + button = super.createButtonOption(option); + } else { + button = new JButton(option.getText()); + button.addActionListener(e -> doAction(option.getType())); + } + String[] colors = getColorKey(type); + if (option.getType() == 0) { + button.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:999;" + + "margin:3,33,3,33;" + + "borderWidth:0;" + + "focusWidth:0;" + + "innerFocusWidth:0;" + + "default.borderWidth:0;" + + "foreground:$Button.default.foreground;" + + "[light]background:" + colors[0] + ";" + + "[dark]background:" + colors[1] + ";"); + } else { + button.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:999;" + + "margin:3,33,3,33;" + + "borderWidth:1;" + + "focusWidth:0;" + + "innerFocusWidth:1;" + + "background:null;" + + "[light]borderColor:" + colors[0] + ";" + + "[dark]borderColor:" + colors[1] + ";" + + "[light]focusedBorderColor:" + colors[0] + ";" + + "[dark]focusedBorderColor:" + colors[1] + ";" + + "[light]focusColor:" + colors[0] + ";" + + "[dark]focusColor:" + colors[1] + ";" + + "[light]hoverBorderColor:" + colors[0] + ";" + + "[dark]hoverBorderColor:" + colors[1] + ";" + + "[light]foreground:" + colors[0] + ";" + + "[dark]foreground:" + colors[1] + ";"); + } + return button; + } + + protected Icon createIcon(Type type) { + ToastPanel.ThemesData data = Toast.getThemesData().get(asToastType(type)); + FlatSVGIcon icon = new FlatSVGIcon(data.getIcon(), 0.45f); + FlatSVGIcon.ColorFilter colorFilter = new FlatSVGIcon.ColorFilter(); + colorFilter.add(Color.decode("#969696"), Color.decode(data.getColors()[0]), Color.decode(data.getColors()[1])); + icon.setColorFilter(colorFilter); + return icon; + } + + protected String[] getColorKey(Type type) { + if (type == Type.DEFAULT) { + // use accent color as default type + return new String[]{"$Component.accentColor", "$Component.accentColor"}; + } + ToastPanel.ThemesData data = Toast.getThemesData().get(asToastType(type)); + return data.getColors(); + } + + private Toast.Type asToastType(Type type) { + switch (type) { + case DEFAULT: + return Toast.Type.DEFAULT; + case SUCCESS: + return Toast.Type.SUCCESS; + case INFO: + return Toast.Type.INFO; + case WARNING: + return Toast.Type.WARNING; + default: + return Toast.Type.ERROR; + } + } + + public enum Type { + DEFAULT, SUCCESS, INFO, WARNING, ERROR + } +} + diff --git a/src/main/java/backupmanager/Utils/SystemForm.java b/src/main/java/backupmanager/Utils/SystemForm.java new file mode 100644 index 00000000..c382c341 --- /dev/null +++ b/src/main/java/backupmanager/Utils/SystemForm.java @@ -0,0 +1,17 @@ +package backupmanager.Utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SystemForm { + + String name() default ""; + + String description() default ""; + + String[] tags() default {}; +} diff --git a/src/main/java/backupmanager/Utils/ToastUtils.java b/src/main/java/backupmanager/Utils/ToastUtils.java new file mode 100644 index 00000000..621395c4 --- /dev/null +++ b/src/main/java/backupmanager/Utils/ToastUtils.java @@ -0,0 +1,63 @@ +package backupmanager.Utils; + +import java.awt.Component; +import raven.modal.Toast; +import raven.modal.option.Location; +import raven.modal.toast.option.ToastBorderStyle; +import raven.modal.toast.option.ToastLocation; +import raven.modal.toast.option.ToastOption; +import raven.modal.toast.option.ToastStyle; + +public class ToastUtils { + public static void showDefault(Component owner, String text) { + showToast(owner, Toast.Type.DEFAULT, text); + } + + public static void showInfo(Component owner, String text) { + showToast(owner, Toast.Type.INFO, text); + } + + public static void showSuccess(Component owner, String text) { + showToast(owner, Toast.Type.SUCCESS, text); + } + + public static void showWarning(Component owner, String text) { + showToast(owner, Toast.Type.WARNING, text); + } + + public static void showError(Component owner, String text) { + showToast(owner, Toast.Type.ERROR, text); + } + + private static void showToast(Component owner, Toast.Type type, String text) { + Toast.show(owner, type, text, getSelectedOption()); + } + + private static ToastOption getSelectedOption() { + ToastOption option = Toast.createOption(); + Location h = Location.CENTER; + Location v = Location.TOP; + ToastStyle.BackgroundType backgroundType = ToastStyle.BackgroundType.DEFAULT; + ToastBorderStyle.BorderType borderType = ToastBorderStyle.BorderType.LEADING_LINE; + option.setAnimationEnabled(true) + .setPauseDelayOnHover(true) + .setAutoClose(true) + .setCloseOnClick(true) + .setHeavyWeight(false); + + option.getLayoutOption() + .setLocation(ToastLocation.from(h, v)) + .setRelativeToOwner(false); + option.getStyle().setBackgroundType(backgroundType) + .setShowIcon(true) + .setShowLabel(false) + .setIconSeparateLine(false) + .setShowCloseButton(true) + .setPaintTextColor(false) + .setPromiseLabel("Saving...") + .getBorderStyle() + .setBorderType(borderType) + ; + return option; + } +} diff --git a/src/main/java/backupmanager/Utils/UndoRedo.java b/src/main/java/backupmanager/Utils/UndoRedo.java new file mode 100644 index 00000000..4deaaf8c --- /dev/null +++ b/src/main/java/backupmanager/Utils/UndoRedo.java @@ -0,0 +1,94 @@ +package backupmanager.Utils; + +import java.util.Iterator; +import java.util.Stack; + +public class UndoRedo<E> implements Iterable<E> { + + private final Stack<E> stack1; + private final Stack<E> stack2; + + public UndoRedo() { + stack1 = new Stack<>(); + stack2 = new Stack<>(); + } + + public void add(E item) { + stack1.push(item); + stack2.clear(); + } + + public E undo() { + if (stack1.size() > 1) { + stack2.push(stack1.pop()); + return stack1.get(stack1.size() - 1); + } else { + return null; + } + } + + public E redo() { + if (!stack2.isEmpty()) { + E item = stack2.pop(); + stack1.push(item); + return item; + } else { + return null; + } + } + + public E getCurrent() { + if (stack1.isEmpty()) { + return null; + } else { + return stack1.get(stack1.size() - 1); + } + } + + public boolean isUndoAble() { + return stack1.size() > 1; + } + + public boolean isRedoAble() { + return !stack2.empty(); + } + + public void clear() { + stack1.clear(); + stack2.clear(); + } + + public void clearRedo() { + stack2.clear(); + } + + @Override + public Iterator<E> iterator() { + return new MyIterator(); + } + + private class MyIterator implements Iterator<E> { + + private int index = 0; + + @Override + public boolean hasNext() { + if (index < stack1.size()) { + return true; + } else if (index < stack1.size() + stack2.size()) { + return true; + } else { + return false; + } + } + + @Override + public E next() { + if (index < stack1.size()) { + return stack1.elementAt(index++); + } else { + return stack2.elementAt((index++) - stack1.size()); + } + } + } +} diff --git a/src/main/java/backupmanager/Utils/table/CheckBoxTableHeaderRenderer.java b/src/main/java/backupmanager/Utils/table/CheckBoxTableHeaderRenderer.java new file mode 100644 index 00000000..13ef3142 --- /dev/null +++ b/src/main/java/backupmanager/Utils/table/CheckBoxTableHeaderRenderer.java @@ -0,0 +1,86 @@ +package backupmanager.Utils.table; + +import com.formdev.flatlaf.FlatClientProperties; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class CheckBoxTableHeaderRenderer extends JCheckBox implements TableCellRenderer { + + private final JTable table; + private final int column; + private final TableCellRenderer oldCellRenderer; + + public CheckBoxTableHeaderRenderer(JTable table, int column) { + this.table = table; + this.column = column; + this.oldCellRenderer = table.getTableHeader().getDefaultRenderer(); + init(); + } + + private void init() { + putClientProperty(FlatClientProperties.STYLE, "" + + "background:null;"); + setHorizontalAlignment(SwingConstants.CENTER); + + table.getTableHeader().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent me) { + if (SwingUtilities.isLeftMouseButton(me)) { + int col = table.columnAtPoint(me.getPoint()); + if (col == column) { + putClientProperty(FlatClientProperties.SELECTED_STATE, null); + setSelected(!isSelected()); + selectedTableRow(isSelected()); + } + } + } + }); + + table.getModel().addTableModelListener((tme) -> { + if (tme.getColumn() == column || tme.getType() == TableModelEvent.DELETE) { + checkRow(); + } + }); + } + + private void checkRow() { + boolean initValue = table.getRowCount() == 0 ? false : (boolean) table.getValueAt(0, column); + for (int i = 1; i < table.getRowCount(); i++) { + boolean v = (boolean) table.getValueAt(i, column); + if (initValue != v) { + putClientProperty(FlatClientProperties.SELECTED_STATE, FlatClientProperties.SELECTED_STATE_INDETERMINATE); + table.getTableHeader().repaint(); + return; + } + } + putClientProperty(FlatClientProperties.SELECTED_STATE, null); + setSelected(initValue); + table.getTableHeader().repaint(); + } + + private void selectedTableRow(boolean selected) { + for (int i = 0; i < table.getRowCount(); i++) { + table.setValueAt(selected, i, column); + } + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JComponent com = (JComponent) oldCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBorder(com.getBorder()); + return this; + } + + @Override + protected void paintComponent(Graphics g) { + if (getBorder() != null) { + getBorder().paintBorder(this, g, 0, 0, getWidth(), getHeight()); + } + super.paintComponent(g); + } +} diff --git a/src/main/java/backupmanager/Table/CheckboxCellRenderer.java b/src/main/java/backupmanager/Utils/table/CheckboxCellRenderer.java similarity index 97% rename from src/main/java/backupmanager/Table/CheckboxCellRenderer.java rename to src/main/java/backupmanager/Utils/table/CheckboxCellRenderer.java index 43f0dd73..898e3254 100644 --- a/src/main/java/backupmanager/Table/CheckboxCellRenderer.java +++ b/src/main/java/backupmanager/Utils/table/CheckboxCellRenderer.java @@ -1,4 +1,4 @@ -package backupmanager.Table; +package backupmanager.Utils.table; import java.awt.Color; import java.awt.Component; diff --git a/src/main/java/backupmanager/Utils/table/ProgressBarRenderer.java b/src/main/java/backupmanager/Utils/table/ProgressBarRenderer.java new file mode 100644 index 00000000..825e977b --- /dev/null +++ b/src/main/java/backupmanager/Utils/table/ProgressBarRenderer.java @@ -0,0 +1,25 @@ +package backupmanager.Utils.table; + +import java.awt.Component; + +import javax.swing.JProgressBar; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +public class ProgressBarRenderer extends DefaultTableCellRenderer { + private final JProgressBar progressBar = new JProgressBar(0, 100); + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + // If the value is an Integer (assuming progress data), show the progress bar + if (value instanceof Integer integer) { + progressBar.setValue(integer); + progressBar.setString(integer + "%"); + + return progressBar; + } + + // Return the default (striped) component for non-progress values + return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } +} diff --git a/src/main/java/backupmanager/Utils/table/TableHeaderAlignment.java b/src/main/java/backupmanager/Utils/table/TableHeaderAlignment.java new file mode 100644 index 00000000..1e4dc981 --- /dev/null +++ b/src/main/java/backupmanager/Utils/table/TableHeaderAlignment.java @@ -0,0 +1,35 @@ +package backupmanager.Utils.table; + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; +import java.awt.*; + +public class TableHeaderAlignment implements TableCellRenderer { + + private final TableCellRenderer headerDelegate; + private final TableCellRenderer cellDelegate; + + public TableHeaderAlignment(JTable table) { + this.headerDelegate = table.getTableHeader().getDefaultRenderer(); + this.cellDelegate = table.getDefaultRenderer(Object.class); + table.setDefaultRenderer(Object.class, new TableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable jtable, Object o, boolean bln, boolean bln1, int row, int column) { + JLabel label = (JLabel) cellDelegate.getTableCellRendererComponent(jtable, o, bln, bln1, row, column); + label.setHorizontalAlignment(getAlignment(column)); + return label; + } + }); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel) headerDelegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + label.setHorizontalAlignment(getAlignment(column)); + return label; + } + + protected int getAlignment(int column) { + return SwingConstants.CENTER; + } +} diff --git a/src/main/java/backupmanager/Widgets/SideMenuPanel.java b/src/main/java/backupmanager/Widgets/SideMenuPanel.java deleted file mode 100644 index c0261d48..00000000 --- a/src/main/java/backupmanager/Widgets/SideMenuPanel.java +++ /dev/null @@ -1,263 +0,0 @@ -package backupmanager.Widgets; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @class SideMenuPanel - * @brief Provides a responsive animated side menu for Swing applications. - */ -public class SideMenuPanel { - - private int minWidth = 60; - private int maxWidth = 200; - private int speed = 2; - private int responsiveMinWidth = 600; - - private JPanel side; - private JComponent main; - private boolean mainAnimationEnabled = false; - private boolean isOpen = false; - private int currentWidth = 0; - - private final JFrame frame; - - /** - * @brief Constructs a SideMenuPanel attached to a given JFrame. - * @param frame The parent frame hosting the side and main panels. - */ - public SideMenuPanel(JFrame frame) { - this.frame = frame; - this.frame.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - currentWidth = 0; - } - }); - } - - /** @return Whether the side menu is currently open. */ - public boolean isOpen() { - return isOpen; - } - - /** - * @brief Manually sets the open state of the side menu. - * @param open True to set the menu as open; false to set as closed. - */ - public void setOpen(boolean open) { - this.isOpen = open; - } - - /** @return The minimum frame width required to enable main panel shifting. */ - public int getResponsiveMinWidth() { - return responsiveMinWidth; - } - - /** - * @brief Sets the minimum frame width that enables responsive main panel shifting. - * @param responsiveWidth Minimum width in pixels. - */ - public void setResponsiveMinWidth(int responsiveWidth) { - this.responsiveMinWidth = responsiveWidth; - } - - /** @return The animation speed in pixels per frame. */ - public int getSpeed() { - return speed; - } - - /** - * @brief Sets the animation speed. - * @param speed Speed in pixels per frame. If set to 0, uses maxWidth as speed. - */ - public void setSpeed(int speed) { - this.speed = (speed == 0) ? maxWidth : speed; - } - - /** @return The minimum width of the sidebar. */ - public int getMinWidth() { - return minWidth; - } - - /** - * @brief Sets the minimum sidebar width. - * @param minWidth Width in pixels. - */ - public void setMinWidth(int minWidth) { - this.minWidth = minWidth; - } - - /** @return The maximum width the sidebar can expand to. */ - public int getMaxWidth() { - return maxWidth; - } - - /** - * @brief Sets the maximum width of the sidebar. - * @param maxWidth Width in pixels. - */ - public void setMaxWidth(int maxWidth) { - this.maxWidth = maxWidth; - } - - /** @return The sidebar panel. */ - public JPanel getSide() { - return side; - } - - /** - * @brief Sets the sidebar panel. - * @param side A JPanel representing the sidebar. - */ - public void setSide(JPanel side) { - this.side = side; - } - - /** @return The main content panel. */ - public JComponent getMain() { - return main; - } - - /** - * @brief Sets the main content panel. Can be null for no responsiveness. - * @param main A JPanel or null. - */ - public void setMain(JComponent main) { - this.main = main; - } - - /** @return Whether the main panel moves with the sidebar. */ - public boolean isMainAnimationEnabled() { - return mainAnimationEnabled; - } - - /** - * @brief Enables or disables main panel animation on sidebar toggle. - * @param enabled True to enable animation; false otherwise. - */ - public void setMainAnimationEnabled(boolean enabled) { - this.mainAnimationEnabled = enabled; - } - - /** - * @brief Toggles the sidebar open or closed with animation. - * The main panel is shifted only if set and enabled. - */ - public void toggleMenu() { - if (side == null) { - System.err.println("Error: sidebar can't be null"); - return; - } - - if (currentWidth == maxWidth) { - animateMenu(false); // close - } else { - animateMenu(true); // open - } - } - - /** - * @brief Animates the sidebar either opening or closing. - * @param opening True to open; false to close. - */ - private void animateMenu(boolean opening) { - int direction = opening ? 1 : -1; - int target = opening ? maxWidth : 0; - int b = maxWidth % speed; - - new Thread(() -> { - try { - for (int i = currentWidth; opening ? i <= maxWidth : i >= 0; i += direction * speed) { - if (!opening && i <= b) i = 0; - if (opening && i >= maxWidth - b) i = maxWidth; - - updatePanelSize(i); - TimeUnit.NANOSECONDS.sleep(1); - } - currentWidth = target; - isOpen = opening; - } catch (InterruptedException e) { - Logger.getLogger(SideMenuPanel.class.getName()).log(Level.SEVERE, null, e); - } - }).start(); - } - - /** - * @brief Updates the width of the sidebar and, optionally, shifts the main panel. - * @param delta Width to add to minWidth for the sidebar. - */ - private void updatePanelSize(int delta) { - int width = minWidth + delta; - - side.setSize(new Dimension(width, side.getHeight())); - for (Component child : side.getComponents()) { - child.setSize(new Dimension(width, child.getHeight())); - } - - if (main != null && frame.getWidth() >= responsiveMinWidth && mainAnimationEnabled) { - main.setLocation(width, main.getY()); - } - } - - /** - * @brief Immediately closes the sidebar and resets layout. - */ - public void closeMenu() { - if (isOpen) { - updatePanelSize(0); - if (main != null) { - main.setLocation(minWidth, main.getY()); - updateLayout(minWidth); - } - isOpen = false; - currentWidth = 0; - } - } - - /** - * @brief Immediately opens the sidebar and updates layout. - */ - public void openMenu() { - if (!isOpen) { - updatePanelSize(maxWidth); - if (main != null) { - main.setLocation(minWidth + maxWidth, main.getY()); - updateLayout(minWidth + maxWidth); - } - currentWidth = maxWidth; - isOpen = true; - } - } - - /** - * @brief Updates the GroupLayout constraints of the main container - * to reflect the sidebar and main panel dimensions. - * @param size The width to apply to the sidebar in the layout. - */ - private void updateLayout(int size) { - if (main == null || main.getParent() == null) return; - - Container parent = main.getParent(); - GroupLayout layout = new GroupLayout(parent); - parent.setLayout(layout); - - layout.setHorizontalGroup( - layout.createSequentialGroup() - .addComponent(side, size, size, size) - .addGap(0) - .addComponent(main, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(side, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(main, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - } -} diff --git a/src/main/java/backupmanager/ZipFileVisitor.java b/src/main/java/backupmanager/ZipFileVisitor.java index 031d7025..a0a47fdf 100644 --- a/src/main/java/backupmanager/ZipFileVisitor.java +++ b/src/main/java/backupmanager/ZipFileVisitor.java @@ -85,7 +85,7 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce private boolean isZippingThreadInterrupted() { if (Thread.currentThread().isInterrupted()) { - RunningBackupService.updateBackupStatusAfterForceTerminationByBackupConfigurationId(context.backup().getId()); + RunningBackupService.updateBackupStatusAfterForceTerminationByBackupConfigurationId(context.execution().backup().getId()); logger.info("Zipping process manually interrupted"); return true; } diff --git a/src/main/java/backupmanager/customwidgets/ModernTextField.java b/src/main/java/backupmanager/customwidgets/ModernTextField.java deleted file mode 100644 index 455a9ca7..00000000 --- a/src/main/java/backupmanager/customwidgets/ModernTextField.java +++ /dev/null @@ -1,162 +0,0 @@ -package backupmanager.customwidgets; - -import java.awt.Color; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.RenderingHints; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.geom.Rectangle2D; - -import javax.swing.JTextField; -import javax.swing.border.EmptyBorder; - -import org.jdesktop.animation.timing.Animator; -import org.jdesktop.animation.timing.TimingTarget; -import org.jdesktop.animation.timing.TimingTargetAdapter; - -public class ModernTextField extends JTextField { - - private String labelText = "Label"; - private String hintText = "Label"; - private final Color lineColor = new Color(3, 155, 216); - private final Animator animator; - private boolean animateHinText = true; - private float location; - private boolean show; - private boolean mouseOver = false; - - public ModernTextField() { - setBorder(new EmptyBorder(20, 3, 10, 3)); - setSelectionColor(new Color(76, 204, 255)); - - addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent me) { - mouseOver = true; - repaint(); - } - - @Override - public void mouseExited(MouseEvent me) { - mouseOver = false; - repaint(); - } - }); - - addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent fe) { - showing(false); - } - - @Override - public void focusLost(FocusEvent fe) { - showing(true); - } - }); - - TimingTarget target = new TimingTargetAdapter() { - @Override - public void begin() { - animateHinText = getText().isEmpty(); - } - - @Override - public void timingEvent(float fraction) { - location = fraction; - repaint(); - } - }; - - animator = new Animator(300, target); - animator.setResolution(0); - animator.setAcceleration(0.5f); - animator.setDeceleration(0.5f); - } - - private void showing(boolean action) { - if (animator.isRunning()) { - animator.stop(); - } else { - location = 1; - } - animator.setStartFraction(1f - location); - show = action; - location = 1f - location; - animator.start(); - } - - @Override - public void paint(Graphics grphcs) { - super.paint(grphcs); - Graphics2D g2 = (Graphics2D) grphcs; - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); - - int width = getWidth(); - int height = getHeight(); - - // Line color adjustment based on mouse hover state - g2.setColor(mouseOver ? lineColor : new Color(150, 150, 150)); - g2.fillRect(2, height - 1, width - 4, 1); - - // Draw the hint text and line style - createHintText(g2); - createLineStyle(g2); - - g2.dispose(); - } - - private void createHintText(Graphics2D g2) { - Insets in = getInsets(); - g2.setColor(new Color(150, 150, 150)); - FontMetrics ft = g2.getFontMetrics(); - Rectangle2D r2 = ft.getStringBounds(labelText, g2); - - double height = getHeight() - in.top - in.bottom; - double textY = (height - r2.getHeight()) / 2; - double size = animateHinText ? (show ? 18 * (1 - location) : 18 * location) : 18; - - g2.drawString(hintText, in.right, (int) (in.top + textY + ft.getAscent() - size)); - } - - private void createLineStyle(Graphics2D g2) { - if (isFocusOwner()) { - double width = getWidth() - 4; - int height = getHeight(); - g2.setColor(lineColor); - - double size = show ? width * (1 - location) : width * location; - double x = (width - size) / 2; - g2.fillRect((int) (x + 2), height - 2, (int) size, 2); - } - } - - @Override - public void setText(String string) { - boolean wasEmpty = getText().isEmpty(); - super.setText(string); - if (wasEmpty != string.isEmpty()) { - showing(string.isEmpty()); - } - } - - public void setHintText(String string) { - this.hintText = string; - repaint(); - } - - public String getLabelText() { - return labelText; - } - - public void setLabelText(String labelText) { - this.labelText = labelText; - repaint(); - } -} diff --git a/src/main/java/backupmanager/database/DatabasePaths.java b/src/main/java/backupmanager/database/DatabasePaths.java index d68405ca..738dec90 100644 --- a/src/main/java/backupmanager/database/DatabasePaths.java +++ b/src/main/java/backupmanager/database/DatabasePaths.java @@ -14,5 +14,4 @@ public static Path getProductionDatabasePath() { public static Path getTestDatabasePath() { return Paths.get("data", "BackupManager.db"); } - } diff --git a/src/main/java/backupmanager/database/Repositories/BackupConfigurationRepository.java b/src/main/java/backupmanager/database/Repositories/BackupConfigurationRepository.java index c4863267..486e7218 100644 --- a/src/main/java/backupmanager/database/Repositories/BackupConfigurationRepository.java +++ b/src/main/java/backupmanager/database/Repositories/BackupConfigurationRepository.java @@ -16,6 +16,7 @@ import backupmanager.Entities.ConfigurationBackup; import backupmanager.Entities.TimeInterval; +import backupmanager.Exceptions.BackupDeletionException; import backupmanager.Helpers.SqlHelper; import backupmanager.Managers.ExceptionManager; import backupmanager.database.Database; @@ -91,7 +92,7 @@ public static void updateBackup(ConfigurationBackup backup) { } } - public static void deleteBackup(int backupId) { + public static void deleteBackup(int backupId) throws BackupDeletionException { String sql = "DELETE FROM BackupConfigurations WHERE BackupId = ?"; try (Connection conn = Database.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { @@ -102,8 +103,10 @@ public static void deleteBackup(int backupId) { logger.info("Backup deleted succesfully"); } catch (SQLException e) { - logger.error("Backup configuration deleting error: " + e.getMessage()); + String error = "Backup configuration deleting error: " + e.getMessage(); + logger.error(error); ExceptionManager.openExceptionMessage(e.getMessage(), Arrays.toString(e.getStackTrace())); + throw new BackupDeletionException(error, e); } } diff --git a/src/main/java/backupmanager/database/Repositories/BackupRequestRepository.java b/src/main/java/backupmanager/database/Repositories/BackupRequestRepository.java index 451add3d..968d88c6 100644 --- a/src/main/java/backupmanager/database/Repositories/BackupRequestRepository.java +++ b/src/main/java/backupmanager/database/Repositories/BackupRequestRepository.java @@ -103,7 +103,6 @@ public static List<BackupRequest> getRunningBackups() { BackupTriggerType triggeredBy = BackupTriggerType.fromCode(triggeredByInt); backups.add(new BackupRequest(backupRequestId, backupConfigurationId, startedDate, completionDate, status, progress, triggeredBy, durationMs, outputPath, unzippedTargetSize, zippedTargetSize, filesCount, errorMessage)); - logger.debug("Loaded running backup: backupRequestId={} configurationId={}", backupRequestId, backupConfigurationId); } } @@ -163,7 +162,6 @@ public static List<BackupRequest> getRequestBackups() { BackupStatus status = BackupStatus.fromCode(statusInt); backups.add(new BackupRequest(backupRequestId, backupConfigurationId, startedDate, completionDate, status, progress, triggeredBy, durationMs, outputPath, unzippedTargetSize, zippedTargetSize, filesCount, errorMessage)); - logger.debug("Loaded running backup: backupRequestId={} configurationId={}", backupRequestId, backupConfigurationId); } } diff --git a/src/main/java/backupmanager/database/Repositories/SubscriptionRepository.java b/src/main/java/backupmanager/database/Repositories/SubscriptionRepository.java index 6dce13ed..2e583767 100644 --- a/src/main/java/backupmanager/database/Repositories/SubscriptionRepository.java +++ b/src/main/java/backupmanager/database/Repositories/SubscriptionRepository.java @@ -68,4 +68,32 @@ public static Subscription getAnySubscriptionValid() { return null; } + + // only for unit tests + public static void insertSubscription(Subscription sub) throws SQLException { + String sql = "INSERT INTO Subscriptions (InsertDate, StartDate, EndDate, CreationType) VALUES (?, ?, ?, ?)"; + try (Connection conn = Database.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setLong(1, SqlHelper.toMilliseconds(sub.insertDate())); + stmt.setLong(2, SqlHelper.toMilliseconds(sub.startDate())); + stmt.setLong(3, SqlHelper.toMilliseconds(sub.endDate())); + stmt.setString(4, sub.creationType().name()); + stmt.executeUpdate(); + } catch (SQLException e) { + throw new SQLException("Subscription inserting error: " + e.getMessage()); + } + } + + // only for unit tests + public static void deleteSubscriptions() throws SQLException { + String sql = "DELETE FROM Subscriptions"; + try (Connection conn = Database.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.executeUpdate(); + } catch (SQLException e) { + throw new SQLException("Subscription deletion error: " + e.getMessage()); + } + } } diff --git a/src/main/java/backupmanager/gui/Controllers/AppController.java b/src/main/java/backupmanager/gui/Controllers/AppController.java new file mode 100644 index 00000000..17871852 --- /dev/null +++ b/src/main/java/backupmanager/gui/Controllers/AppController.java @@ -0,0 +1,91 @@ +package backupmanager.gui.Controllers; + +import java.awt.Frame; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import backupmanager.BackupOperations; +import backupmanager.Enums.SubscriptionStatus; +import backupmanager.Helpers.SubscriptionHelper; +import backupmanager.Helpers.SubscriptionNotifier; +import backupmanager.MainApp; +import backupmanager.Services.BackgroundService; +import backupmanager.gui.frames.BackupManager; + +public class AppController { + private static final Logger logger = LoggerFactory.getLogger(AppController.class); + + private final BackgroundService backgroundService; + private final TrayController trayController; + + public static AppController startBackgroundProcess() throws IOException { + return new AppController(); + } + + private AppController() throws IOException { + logger.info("Starting BackupManager application"); + + this.backgroundService = new BackgroundService(); + + this.trayController = new TrayController( + this::openGui, + this::exitApp + ); + + BackupOperations.deletePotentiallyIncompletedBackupsFromLastExecution(); + + trayController.start(); + + if (canBackgroundServiceStartsBasedOnSubscription()) { + logger.info("Backup service starting in the background"); + backgroundService.start(this.trayController); + } + } + + private boolean canBackgroundServiceStartsBasedOnSubscription() { + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + showSubscriptionNotificationIfNeeded(status); + return status != SubscriptionStatus.EXPIRED; + } + + private void showSubscriptionNotificationIfNeeded(SubscriptionStatus status) { + switch (status) { + case SubscriptionStatus.EXPIRATION -> { + logger.info("Subscription is expiring alert"); + SubscriptionNotifier.showExpiringWarning(trayController); + } + case SubscriptionStatus.EXPIRED -> { + logger.info("Subscription expired alert"); + SubscriptionNotifier.showExpiredAlert(trayController); + } + case ACTIVE, NONE -> { } + } + } + + private void openGui() { + logger.info("Opening main GUI"); + + java.awt.EventQueue.invokeLater(() -> { + MainApp.initLaf(); + + BackupManager frame = BackupManager.getInstance(); + + frame.setVisible(true); + frame.toFront(); + frame.requestFocus(); + + if (frame.getState() == Frame.ICONIFIED) { + frame.setState(Frame.NORMAL); + } + }); + } + + private void exitApp() { + logger.info("Exiting application"); + + backgroundService.stop(); + System.exit(0); + } +} diff --git a/src/main/java/backupmanager/Controllers/BackupEntryController.java b/src/main/java/backupmanager/gui/Controllers/BackupEntryController.java similarity index 58% rename from src/main/java/backupmanager/Controllers/BackupEntryController.java rename to src/main/java/backupmanager/gui/Controllers/BackupEntryController.java index 8b0f1f7f..a2d6e7db 100644 --- a/src/main/java/backupmanager/Controllers/BackupEntryController.java +++ b/src/main/java/backupmanager/gui/Controllers/BackupEntryController.java @@ -1,26 +1,33 @@ -package backupmanager.Controllers; +package backupmanager.gui.Controllers; +import java.awt.Component; +import java.io.File; import java.time.LocalDateTime; import javax.swing.JOptionPane; +import javax.swing.JTextField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import backupmanager.BackupOperations; +import backupmanager.Entities.BackupExecutionContext; +import backupmanager.Entities.BackupUIContext; import backupmanager.Entities.ConfigurationBackup; import backupmanager.Entities.TimeInterval; import backupmanager.Entities.ZippingContext; import backupmanager.Enums.BackupTriggerType; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; import backupmanager.Exceptions.BackupAlreadyRunningException; +import backupmanager.Exceptions.BackupDeletionException; import backupmanager.Exceptions.InvalidTimeInterval; -import backupmanager.GUI.BackupManagerGUI; -import backupmanager.GUI.BackupProgressGUI; import backupmanager.Helpers.BackupHelper; -import backupmanager.Table.BackupTable; +import backupmanager.Utils.ToastUtils; import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.gui.Table.BackupTableDataService; +import backupmanager.gui.frames.BackupProgressGUI; +import backupmanager.gui.simple.TimePickerDialog; public class BackupEntryController { private static final Logger logger = LoggerFactory.getLogger(BackupEntryController.class); @@ -59,8 +66,8 @@ public ConfigurationBackup getBackup(String name, String initialPath, String des } } - public TimeInterval handleTimePickerAction(javax.swing.JDialog dialog, String target, String destination) throws InvalidTimeInterval { - TimeInterval time = BackupHelper.openTimePicker(dialog, currentBackup.getTimeIntervalBackup()); + public TimeInterval handleTimePickerAction(TimePickerDialog picker, String target, String destination) throws InvalidTimeInterval { + TimeInterval time = BackupHelper.openTimePicker(picker); if (time == null) throw new InvalidTimeInterval(); LocalDateTime nextDateBackup = BackupHelper.getNexDateBackup(time); @@ -73,21 +80,25 @@ public TimeInterval handleTimePickerAction(javax.swing.JDialog dialog, String ta return time; } - public boolean canDisposeAfterOk(String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep, boolean create) { - if (name.isBlank() || destinationPath.isBlank() || initialPath.isBlank()) + public boolean canDisposeAfterOk(Component owner, String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep, boolean create) throws BackupDeletionException { + if (name.isBlank() || destinationPath.isBlank() || initialPath.isBlank()) { + ToastUtils.showError(owner, Translations.get(TKey.TOAST_BACKUP_PATHS_EMPTY_ERROR)); return false; + } - currentBackup = getBackup(name, initialPath, destinationPath, notes, autoBackup, maxBackupsToKeep); + updateCurrentBackup(name, initialPath, destinationPath, notes, autoBackup, maxBackupsToKeep); if (create) { if (ConfigurationBackup.getBackupByName(currentBackup.getName()) != null) { - int response = JOptionPane.showConfirmDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.DUPLICATED_BACKUP_NAME_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + int response = JOptionPane.showConfirmDialog(null, Translations.get(TKey.DUPLICATED_BACKUP_NAME_MESSAGE), Translations.get(TKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (response == JOptionPane.YES_OPTION) { - BackupHelper.removeBackup(currentBackup.getName()); - } else { - return false; + BackupHelper.deleteBackup(currentBackup.getName()); + ToastUtils.showInfo(owner, Translations.get(TKey.TOAST_BACKUP_EDITED)); } + else + return false; } + BackupHelper.newBackup(currentBackup); } else { BackupHelper.updateBackup(currentBackup); @@ -96,11 +107,19 @@ public boolean canDisposeAfterOk(String name, String initialPath, String destina return true; } - public boolean toggleAutomaticBackup(String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep) { - currentBackup = getBackup(name, initialPath, destinationPath, notes, autoBackup, maxBackupsToKeep); + public void openFileChooser(JTextField filed, boolean allowFiles) { + String text = BackupOperations.pathSearchWithFileChooser(allowFiles); + if (text != null) { + filed.setText(text); + } + } + + public boolean toggleAutomaticBackup(Component parent, String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep) { + updateCurrentBackup(name, initialPath, destinationPath,notes, autoBackup, maxBackupsToKeep); + currentBackup.setAutomatic(!currentBackup.isAutomatic()); - ConfigurationBackup backup = BackupHelper.toggleAutomaticBackup(currentBackup); + ConfigurationBackup backup = BackupHelper.toggleAutomaticBackup(parent, currentBackup); if (backup == null) return false; @@ -116,21 +135,28 @@ public boolean toggleAutomaticBackup(String name, String initialPath, String des return false; } - public void handleSingleBackupRequest(BackupTable backupTable, String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep) throws BackupAlreadyRunningException { + public void handleSingleBackupRequest(BackupTableDataService backupTable, String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep) throws BackupAlreadyRunningException { if (BackupRequestRepository.isAnyBackupRunning()) { - JOptionPane.showMessageDialog(null, TranslationCategory.DIALOGS.getTranslation(TranslationKey.WARNING_BACKUP_ALREADY_IN_PROGRESS_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.WARNING_GENERIC_TITLE), JOptionPane.WARNING_MESSAGE); throw new BackupAlreadyRunningException(); } - // update currentBackup - if (currentBackup == null) { - currentBackup = getBackup(name, initialPath, destinationPath, notes, autoBackup, maxBackupsToKeep); + currentBackup = getBackup( + name, + initialPath, + destinationPath, + notes, + autoBackup, + maxBackupsToKeep + ); + + if (ConfigurationBackup.getBackupByName(currentBackup.getName()) == null) { + BackupHelper.newBackup(currentBackup); } singleBackup(initialPath, destinationPath, backupTable); } - private void singleBackup(String target, String destination, BackupTable backupTable) { + private void singleBackup(String target, String destination, BackupTableDataService backupTable) { logger.info("Event --> single backup"); String path1 = target; @@ -138,22 +164,14 @@ private void singleBackup(String target, String destination, BackupTable backupT currentBackup.setTargetPath(path2); - String temp = "\\"; - if (!BackupOperations.checkInputCorrect(currentBackup.getName(), path1, path2, null)) return; LocalDateTime dateNow = LocalDateTime.now(); - String name1; // folder name/initial file String date = dateNow.format(BackupHelper.dateForfolderNameFormatter); //------------------------------SET ALL THE STRINGS------------------------------ - 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; - } + String name1 = new File(path1).getName(); name1 = BackupOperations.removeExtension(name1); path2 = path2 + "\\" + name1 + " (Backup " + date + ")"; @@ -161,10 +179,13 @@ private void singleBackup(String target, String destination, BackupTable backupT //------------------------------COPY THE FILE OR DIRECTORY------------------------------ logger.info("date backup: " + date); - BackupManagerGUI.progressBar = new BackupProgressGUI(path1, path2); - BackupManagerGUI.progressBar.setVisible(true); + BackupProgressGUI progressBar = new BackupProgressGUI(path1, path2); + progressBar.setVisible(true); - ZippingContext context = ZippingContext.create(currentBackup, null, backupTable, BackupManagerGUI.progressBar, null, null); + ZippingContext context = new ZippingContext( + BackupExecutionContext.create(currentBackup), + new BackupUIContext(null, backupTable, progressBar, null, null) + ); BackupOperations.executeBackup(context, BackupTriggerType.USER, path1, path2); @@ -176,16 +197,34 @@ private void singleBackup(String target, String destination, BackupTable backupT } } - public void handleOpenBackupActivationMessage(TimeInterval newtimeInterval, String target, String destination) { + public void handleOpenBackupActivationMessage(Component parent, TimeInterval newtimeInterval, String target, String destination) { currentBackup.setTimeIntervalBackup(newtimeInterval); - BackupHelper.showMessageActivationAutoBackup(newtimeInterval, target, destination); + BackupHelper.showMessageActivationAutoBackup(parent, newtimeInterval, target, destination); } public ConfigurationBackup getCurrentBackup() { return currentBackup; } - public void setCurrentBackup(ConfigurationBackup currentBackup) { - this.currentBackup = currentBackup; + private void updateCurrentBackup(String name, String initialPath, String destinationPath, String notes, boolean autoBackup, int maxBackupsToKeep) { + if (currentBackup == null) { + currentBackup = getBackup( + name, + initialPath, + destinationPath, + notes, + autoBackup, + maxBackupsToKeep + ); + return; + } + + currentBackup.setName(name); + currentBackup.setTargetPath(initialPath); + currentBackup.setDestinationPath(destinationPath); + currentBackup.setNotes(notes); + currentBackup.setAutomatic(autoBackup); + currentBackup.setMaxToKeep(maxBackupsToKeep); + currentBackup.setLastUpdateDate(LocalDateTime.now()); } } diff --git a/src/main/java/backupmanager/gui/Controllers/EntryUserController.java b/src/main/java/backupmanager/gui/Controllers/EntryUserController.java new file mode 100644 index 00000000..535f3b06 --- /dev/null +++ b/src/main/java/backupmanager/gui/Controllers/EntryUserController.java @@ -0,0 +1,22 @@ +package backupmanager.gui.Controllers; + +import javax.swing.JComponent; + +import backupmanager.Email.EmailValidator; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Utils.ToastUtils; + +public class EntryUserController { + public boolean isInputOkAndShowErrorIfNecessary(JComponent component, String name, String surname, String email) { + if (name.isEmpty() || surname.isEmpty() || email.isEmpty()) { + ToastUtils.showError(component, Translations.get(TKey.TOAST_MISSING_DATA_LOGIN_ERROR)); + return false; + } else if (!EmailValidator.isValidEmail(email)) { + ToastUtils.showError(component, Translations.get(TKey.TOAST_WRONG_EMAIL_LOGIN_ERROR)); + return false; + } + ToastUtils.showSuccess(component, Translations.get(TKey.TOAST_LOGIN)); + return true; + } +} diff --git a/src/main/java/backupmanager/Controllers/GuiController.java b/src/main/java/backupmanager/gui/Controllers/GuiController.java similarity index 87% rename from src/main/java/backupmanager/Controllers/GuiController.java rename to src/main/java/backupmanager/gui/Controllers/GuiController.java index fe23c7f6..f9561b6a 100644 --- a/src/main/java/backupmanager/Controllers/GuiController.java +++ b/src/main/java/backupmanager/gui/Controllers/GuiController.java @@ -1,4 +1,4 @@ -package backupmanager.Controllers; +package backupmanager.gui.Controllers; import java.awt.Image; diff --git a/src/main/java/backupmanager/gui/Controllers/TimePickerController.java b/src/main/java/backupmanager/gui/Controllers/TimePickerController.java new file mode 100644 index 00000000..ff401ded --- /dev/null +++ b/src/main/java/backupmanager/gui/Controllers/TimePickerController.java @@ -0,0 +1,40 @@ +package backupmanager.gui.Controllers; + +import javax.swing.JOptionPane; + +import backupmanager.Entities.TimeInterval; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Utils.ToastUtils; +import backupmanager.gui.simple.TimePickerDialog; + +public class TimePickerController { + + public TimePickerController() { } + + public TimeInterval getTimeIntervalIfPossible(TimePickerDialog dialog, int days, int hours, int minutes) { + if (isLongTimeCorrect(days, hours, minutes)) { + if (isShortTimeCorrect(days, hours) && !showWarningMessageForShortTimeAndGetIfItOkayResponse(null)) + return null; + + return new TimeInterval(days, hours, minutes); + } + else + ToastUtils.showError(dialog, Translations.get(TKey.TOAST_INVALID_TIME)); + return null; + } + + private boolean isLongTimeCorrect(int days, int hours, int minutes) { + return days >= 0 && hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59 && + (days != 0 || hours != 0 || minutes != 0); + } + + private boolean isShortTimeCorrect(int days, int hours) { + return days == 0 && hours == 0; + } + + private boolean showWarningMessageForShortTimeAndGetIfItOkayResponse(javax.swing.JDialog dialog) { + int response = JOptionPane.showConfirmDialog(dialog, Translations.get(TKey.WARNING_SHORT_TIME_INTERVAL_MESSAGE), Translations.get(TKey.WARNING_GENERIC_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + return response == JOptionPane.YES_OPTION; + } +} diff --git a/src/main/java/backupmanager/Controllers/TrayController.java b/src/main/java/backupmanager/gui/Controllers/TrayController.java similarity index 82% rename from src/main/java/backupmanager/Controllers/TrayController.java rename to src/main/java/backupmanager/gui/Controllers/TrayController.java index 85a5a61e..bf6dc174 100644 --- a/src/main/java/backupmanager/Controllers/TrayController.java +++ b/src/main/java/backupmanager/gui/Controllers/TrayController.java @@ -1,4 +1,4 @@ -package backupmanager.Controllers; +package backupmanager.gui.Controllers; import java.awt.AWTException; import java.awt.Image; @@ -15,8 +15,8 @@ import org.slf4j.LoggerFactory; import backupmanager.Enums.ConfigKey; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; public class TrayController { @@ -46,7 +46,7 @@ private void createHiddenIcon() { PopupMenu popup = setupAndGetPopupMenu(); - trayIcon = new TrayIcon(image, "Backup Service", popup); + trayIcon = new TrayIcon(image, Translations.get(TKey.TRAY_TOOLTIP), popup); trayIcon.setImageAutoSize(true); try { @@ -71,13 +71,11 @@ public void mouseClicked(MouseEvent e) { private PopupMenu setupAndGetPopupMenu() { PopupMenu popup = new PopupMenu(); - MenuItem openItem = new MenuItem(TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.OPEN_ACTION)); - MenuItem exitItem = new MenuItem(TranslationCategory.TRAY_ICON.getTranslation(TranslationKey.EXIT_ACTION)); - + MenuItem openItem = new MenuItem(Translations.get(TKey.OPEN_ACTION)); + MenuItem exitItem = new MenuItem(Translations.get(TKey.EXIT_ACTION)); popup.add(openItem); popup.addSeparator(); - popup.addSeparator(); popup.add(exitItem); openItem.addActionListener(e -> onOpen.run()); diff --git a/src/main/java/backupmanager/Table/BackupTable.java b/src/main/java/backupmanager/gui/Table/BackupTable.java similarity index 98% rename from src/main/java/backupmanager/Table/BackupTable.java rename to src/main/java/backupmanager/gui/Table/BackupTable.java index 535fa65e..d5ce86c4 100644 --- a/src/main/java/backupmanager/Table/BackupTable.java +++ b/src/main/java/backupmanager/gui/Table/BackupTable.java @@ -1,4 +1,4 @@ -package backupmanager.Table; +package backupmanager.gui.Table; import java.awt.Point; diff --git a/src/main/java/backupmanager/gui/Table/BackupTableDataService.java b/src/main/java/backupmanager/gui/Table/BackupTableDataService.java new file mode 100644 index 00000000..9d6053f3 --- /dev/null +++ b/src/main/java/backupmanager/gui/Table/BackupTableDataService.java @@ -0,0 +1,105 @@ +package backupmanager.gui.Table; + +import java.time.format.DateTimeFormatter; + +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.TableCellRenderer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import backupmanager.Entities.ConfigurationBackup; + +public class BackupTableDataService { + + private final Logger logger = LoggerFactory.getLogger(BackupTableDataService.class); + private final JTable table; + private final DateTimeFormatter formatter; + private final ProgressBarRenderer progressRenderer; + private final TableCellRenderer defaultRenderer; + + private static final int COLUMN_PROGRESS = 3; + + public BackupTableDataService(JTable table, DateTimeFormatter formatter) { + if (table == null) throw new IllegalArgumentException("Table cannot be null"); + if (formatter == null) throw new IllegalArgumentException("Formatter cannot be null"); + this.table = table; + this.formatter = formatter; + this.progressRenderer = new ProgressBarRenderer(); + this.defaultRenderer = table.getColumnModel().getColumn(COLUMN_PROGRESS).getCellRenderer(); + } + + public void removeProgress(ConfigurationBackup backup) { + if (backup == null) throw new IllegalArgumentException("Backup cannot be null"); + + int row = findBackupRowIndex(backup); + + // remove the progress bar renderer + table.getColumnModel().getColumn(COLUMN_PROGRESS).setCellRenderer(defaultRenderer); + + // Set last backup value in the table + table.getModel().setValueAt( + backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "", + row, + COLUMN_PROGRESS + ); + + table.repaint(); // Repaints the whole table + } + + public void updateProgress(ConfigurationBackup backup, int value) { + if (backup == null) throw new IllegalArgumentException("Backup cannot be null"); + if (value < 0 || value > 100) throw new IllegalArgumentException("Value must be between 0 and 100"); + + SwingUtilities.invokeLater(() -> { + // Locate the row index of the backup in the table + int rowIndex = findBackupRowIndex(backup); + if (rowIndex == -1) return; + + table.getColumnModel().getColumn(COLUMN_PROGRESS).setCellRenderer(progressRenderer); + + // Restore the original renderer after completion + if (value == 100) { + logger.debug("Restore the original renderer after completion"); + removeProgress(backup); + } else { + // Update the value of the progress in the table + table.getModel().setValueAt(value, rowIndex, COLUMN_PROGRESS); + } + + table.repaint(); + }); + } + + // public void updateTableWithNewBackupList(List<ConfigurationBackup> updatedBackups) { + // logger.debug("updating backup list"); + + // SwingUtilities.invokeLater(() -> { + // BackupManagerGUI.model.setRowCount(0); + + // for (ConfigurationBackup backup : updatedBackups) { + // BackupManagerGUI.model.addRow(new Object[]{ + // backup.getName(), + // backup.getTargetPath(), + // backup.getDestinationPath(), + // backup.getLastBackupDate() != null ? backup.getLastBackupDate().format(formatter) : "", + // backup.isAutomatic(), + // backup.getNextBackupDate() != null ? backup.getNextBackupDate().format(formatter) : "", + // backup.getTimeIntervalBackup() != null ? backup.getTimeIntervalBackup().toString() : "" + // }); + // } + // }); + // } + + private int findBackupRowIndex(ConfigurationBackup backup) { + if (backup == null) throw new IllegalArgumentException("Backup cannot be null"); + + for (int i = 0; i < table.getRowCount(); i++) { + if (table.getValueAt(i, 0).equals(backup.getName())) { // first column holds unique backup names + return i; + } + } + return -1; + } +} diff --git a/src/main/java/backupmanager/Table/BackupTableModel.java b/src/main/java/backupmanager/gui/Table/BackupTableModel.java similarity index 93% rename from src/main/java/backupmanager/Table/BackupTableModel.java rename to src/main/java/backupmanager/gui/Table/BackupTableModel.java index 0e406580..ad055b9b 100644 --- a/src/main/java/backupmanager/Table/BackupTableModel.java +++ b/src/main/java/backupmanager/gui/Table/BackupTableModel.java @@ -1,4 +1,4 @@ -package backupmanager.Table; +package backupmanager.gui.Table; import javax.swing.table.DefaultTableModel; diff --git a/src/main/java/backupmanager/Table/ProgressBarRenderer.java b/src/main/java/backupmanager/gui/Table/ProgressBarRenderer.java similarity index 52% rename from src/main/java/backupmanager/Table/ProgressBarRenderer.java rename to src/main/java/backupmanager/gui/Table/ProgressBarRenderer.java index d152a374..745cbab4 100644 --- a/src/main/java/backupmanager/Table/ProgressBarRenderer.java +++ b/src/main/java/backupmanager/gui/Table/ProgressBarRenderer.java @@ -1,6 +1,5 @@ -package backupmanager.Table; +package backupmanager.gui.Table; -import java.awt.Color; import java.awt.Component; import javax.swing.JProgressBar; @@ -8,32 +7,21 @@ import javax.swing.table.DefaultTableCellRenderer; public class ProgressBarRenderer extends DefaultTableCellRenderer { - private final StripedRowRenderer stripedRowRenderer = new StripedRowRenderer(); private final JProgressBar progressBar = new JProgressBar(0, 100); @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - // Delegate the striped row coloring logic to the StripedRowRenderer - Component c = stripedRowRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); // If the value is an Integer (assuming progress data), show the progress bar if (value instanceof Integer integer) { progressBar.setValue(integer); progressBar.setString(integer + "%"); progressBar.setStringPainted(true); - - // Set the progress bar background color based on the row (even/odd striped rows) - if (row % 2 == 0) { - progressBar.setBackground(new Color(223, 222, 243)); // Even row color for progress bar - } else { - progressBar.setBackground(Color.WHITE); // Odd row color for progress bar - } - - // Return the progress bar component instead of the default cell component return progressBar; } // Return the default (striped) component for non-progress values return c; } -} \ No newline at end of file +} diff --git a/src/main/java/backupmanager/gui/component/About.java b/src/main/java/backupmanager/gui/component/About.java new file mode 100644 index 00000000..757d971a --- /dev/null +++ b/src/main/java/backupmanager/gui/component/About.java @@ -0,0 +1,103 @@ +package backupmanager.gui.component; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTextPane; +import javax.swing.border.TitledBorder; +import javax.swing.event.HyperlinkEvent; +import javax.swing.text.DefaultCaret; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.Enums.ConfigKey; +import backupmanager.Enums.MenuItems; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Json.JsonConfig; +import backupmanager.Managers.WebsiteManager; +import backupmanager.gui.menu.DrawerManager; +import net.miginfocom.swing.MigLayout; + +public class About extends JPanel { + + public About() { + init(); + } + + private void init() { + + setLayout(new MigLayout("fillx,wrap,insets 20,width 520")); + + JTextPane title = createText(Translations.get(TKey.APP_NAME)); + title.putClientProperty(FlatClientProperties.STYLE, "font:bold +6"); + + JTextPane description = createText(""); + description.setContentType("text/html"); + description.setText(getDescriptionText()); + + description.addHyperlinkListener(e -> { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), e.getURL().toString()); + }); + + add(title); + add(description, "gapy 10"); + add(createSystemInformation(), "gapy 15"); + } + + private JTextPane createText(String text) { + JTextPane pane = new JTextPane(); + pane.setBorder(BorderFactory.createEmptyBorder()); + pane.setText(text); + pane.setEditable(false); + pane.setOpaque(false); + + pane.setCaret(new DefaultCaret() { + @Override + public void paint(java.awt.Graphics g) {} + }); + + return pane; + } + + private String getDescriptionText() { + String message = Translations.get(TKey.ABOUT_MESSAGE_BODY); + message = message.replace("[PROJECT_WEBSITE]", ConfigKey.INFO_PAGE_LINK.getValue()); + + // removing all the info inside the <p> tag + JsonConfig config = JsonConfig.getInstance(); + if (!config.isMenuItemEnabled(MenuItems.Website.name())) { + message = message.replaceAll("<p>.*?</p>", ""); + } + + return message; + } + + private JComponent createSystemInformation() { + + JPanel panel = new JPanel(new MigLayout("wrap,insets 10")); + panel.setBorder(new TitledBorder(Translations.get(TKey.ABOUT_SYSTEM_INFORMATION))); + + JTextPane text = createText(""); + text.setContentType("text/html"); + + String info = """ + <html> + %s: %s<br> + Java: %s<br> + OS: %s<br> + </html> + """.formatted( + Translations.get(TKey.VERSION), + ConfigKey.VERSION.getValue(), + System.getProperty("java.vendor") + " - v" + System.getProperty("java.version"), + System.getProperty("os.name") + " " + System.getProperty("os.arch") + ); + + text.setText(info); + panel.add(text); + + return panel; + } +} diff --git a/src/main/java/backupmanager/gui/component/AccentColorIcon.java b/src/main/java/backupmanager/gui/component/AccentColorIcon.java new file mode 100644 index 00000000..402e40f2 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/AccentColorIcon.java @@ -0,0 +1,37 @@ +package backupmanager.gui.component; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics2D; + +import javax.swing.UIManager; + +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.icons.FlatAbstractIcon; +import com.formdev.flatlaf.util.ColorFunctions; + +public class AccentColorIcon extends FlatAbstractIcon { + + private final String colorKey; + + public AccentColorIcon(String colorKey) { + super(16, 16, null); + this.colorKey = colorKey; + } + + @Override + protected void paintIcon(Component c, Graphics2D g) { + Color color = UIManager.getColor(colorKey); + if (color == null) + color = Color.lightGray; + else if (!c.isEnabled()) { + color = FlatLaf.isLafDark() + ? ColorFunctions.shade(color, 0.5f) + : ColorFunctions.tint(color, 0.6f); + } + + g.setColor(color); + g.fillRoundRect(1, 1, width - 2, height - 2, 5, 5); + g.dispose(); + } +} diff --git a/src/main/java/backupmanager/gui/component/EmptyModalBorder.java b/src/main/java/backupmanager/gui/component/EmptyModalBorder.java new file mode 100644 index 00000000..e31b7a12 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/EmptyModalBorder.java @@ -0,0 +1,65 @@ +package backupmanager.gui.component; + +import java.awt.Color; +import java.awt.Component; + +import net.miginfocom.swing.MigLayout; +import raven.modal.component.Modal; +import raven.modal.component.ModalBorderAction; +import raven.modal.listener.ModalCallback; +import raven.modal.listener.ModalController; + +public class EmptyModalBorder extends Modal implements ModalBorderAction { + + protected final Component component; + public static final int OPENED = 20; + private final ModalCallback callback; + + public EmptyModalBorder(Component component) { + this(component, null); + } + + public EmptyModalBorder(Component component, ModalCallback callback) { + this.component = component; + this.callback = callback; + setLayout(new MigLayout("fill,insets 8 0 8 0", "[fill]", "[fill]")); + add(component); + } + + @Override + protected void modalOpened() { + if (callback != null) { + callback.action(createController(), OPENED); + } + } + + @Override + public void doAction(int action) { + if (callback == null) { + getController().closeModal(); + } else { + ModalController controller = createController(); + callback.action(controller, action); + if (!controller.getConsume()) { + getController().closeModal(); + } + } + } + + @Override + public Color getBackground() { + if (component == null) { + return super.getBackground(); + } + return component.getBackground(); + } + + private ModalController createController() { + return new ModalController(this) { + @Override + public void close() { + getController().closeModal(); + } + }; + } +} diff --git a/src/main/java/backupmanager/gui/component/FormSearchButton.java b/src/main/java/backupmanager/gui/component/FormSearchButton.java new file mode 100644 index 00000000..c8d5c2f0 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/FormSearchButton.java @@ -0,0 +1,39 @@ +package backupmanager.gui.component; + +import javax.swing.JButton; +import javax.swing.JLabel; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import net.miginfocom.swing.MigLayout; + +public class FormSearchButton extends JButton { + + public FormSearchButton() { + super(Translations.get(TKey.QUICK_SEARCH), new FlatSVGIcon("icons/search.svg", 0.4f)); + init(); + } + + private void init() { + setLayout(new MigLayout("insets 0,al trailing,filly", "", "[center]")); + setHorizontalAlignment(JButton.LEADING); + putClientProperty(FlatClientProperties.STYLE, "" + + "margin:5,7,5,10;" + + "arc:10;" + + "borderWidth:0;" + + "focusWidth:0;" + + "innerFocusWidth:0;" + + "[light]background:shade($Panel.background,10%);" + + "[dark]background:tint($Panel.background,10%);" + + "[light]foreground:tint($Button.foreground,40%);" + + "[dark]foreground:shade($Button.foreground,30%);"); + JLabel label = new JLabel("Ctrl F"); + label.putClientProperty(FlatClientProperties.STYLE, "" + + "[light]foreground:tint($Button.foreground,40%);" + + "[dark]foreground:shade($Button.foreground,30%);"); + add(label); + } +} diff --git a/src/main/java/backupmanager/gui/component/FormSearchPanel.java b/src/main/java/backupmanager/gui/component/FormSearchPanel.java new file mode 100644 index 00000000..afed312b --- /dev/null +++ b/src/main/java/backupmanager/gui/component/FormSearchPanel.java @@ -0,0 +1,559 @@ +package backupmanager.gui.component; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JTextField; +import javax.swing.LookAndFeel; +import javax.swing.Scrollable; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.icons.FlatMenuArrowIcon; + +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.gui.menu.MyMenuValidation; +import backupmanager.gui.svg.SVGIconUIColor; +import backupmanager.gui.system.Form; +import backupmanager.gui.system.FormSearch; +import backupmanager.Utils.AppPreferences; +import backupmanager.Utils.SystemForm; +import net.miginfocom.swing.MigLayout; +import raven.modal.Drawer; +import raven.modal.ModalDialog; +import raven.modal.component.ModalContainer; + +public class FormSearchPanel extends JPanel { + + private LookAndFeel oldTheme = UIManager.getLookAndFeel(); + private final int SEARCH_MAX_LENGTH = 50; + private final Map<SystemForm, Class<? extends Form>> formsMap; + private final List<Item> listItems = new ArrayList<>(); + + public FormSearchPanel(Map<SystemForm, Class<? extends Form>> formsMap) { + this.formsMap = formsMap; + init(); + } + + private void init() { + setLayout(new MigLayout("fillx,insets 0,wrap", "[fill,500]")); + textSearch = new JTextField(); + panelResult = new PanelResult(); + textSearch.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.SEARCH_TITLE)); + textSearch.putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_ICON, new FlatSVGIcon("icons/search.svg", 0.4f)); + textSearch.putClientProperty(FlatClientProperties.STYLE, "" + + "border:3,3,3,3;" + + "background:null;" + + "showClearButton:true;"); + add(textSearch, "gap 17 17 0 0"); + add(new JSeparator(), "height 2!"); + JScrollPane scrollPane = new JScrollPane(panelResult); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + scrollPane.getVerticalScrollBar().putClientProperty(FlatClientProperties.STYLE, "" + + "trackArc:$ScrollBar.thumbArc;" + + "thumbInsets:0,3,0,3;" + + "trackInsets:0,3,0,3;" + + "width:12;"); + add(scrollPane); + installSearchField(); + } + + public final void formCheck() { + if (oldTheme != UIManager.getLookAndFeel()) { + oldTheme = UIManager.getLookAndFeel(); + SwingUtilities.updateComponentTreeUI(this); + } + } + + private void installSearchField() { + textSearch.setDocument(new PlainDocument() { + @Override + public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { + if (getLength() + str.length() <= SEARCH_MAX_LENGTH) { + super.insertString(offs, str, a); + } + } + }); + textSearch.getDocument().addDocumentListener(new DocumentListener() { + private String text; + + @Override + public void insertUpdate(DocumentEvent e) { + search(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + search(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + search(); + } + + private void search() { + String st = textSearch.getText().trim().toLowerCase(); // Convert search term to lowercase + if (!st.equals(text)) { + text = st; + panelResult.removeAll(); + listItems.clear(); + if (st.isEmpty()) { + showRecentResult(); + } else { + for (Map.Entry<SystemForm, Class<? extends Form>> entry : formsMap.entrySet()) { + SystemForm s = entry.getKey(); + // Compare both name and description with lower cased search term + if (s.name().toLowerCase().contains(st) + || s.description().toLowerCase().contains(st) + || checkTags(s.tags(), st)) { + if (MyMenuValidation.validation(entry.getValue())) { + Item item = new Item(s, entry.getValue(), false, false); + checkComponentOrientation(item); + panelResult.add(item); + listItems.add(item); + } + } + } + if (!listItems.isEmpty()) { + setSelected(0); + } else { + panelResult.add(createNoResult(st)); + } + panelResult.repaint(); + updateLayout(); + } + } + } + + private boolean checkTags(String[] tags, String st) { + if (tags.length == 0) return false; + return Arrays.stream(tags).anyMatch(s -> s.contains(st)); + } + }); + textSearch.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: + move(true); + break; + case KeyEvent.VK_DOWN: + move(false); + break; + case KeyEvent.VK_ENTER: + showForm(); + break; + } + } + }); + } + + private void updateLayout() { + Container container = SwingUtilities.getAncestorOfClass(ModalContainer.class, FormSearchPanel.this); + if (container != null) { + container.revalidate(); + } + } + + private void showForm() { + int index = getSelectedIndex(); + if (index != -1) { + listItems.get(index).showForm(); + } + } + + private void setSelected(int index) { + for (int i = 0; i < listItems.size(); i++) { + listItems.get(i).setSelected(index == i); + } + } + + private int getSelectedIndex() { + for (int i = 0; i < listItems.size(); i++) { + if (listItems.get(i).isSelected()) { + return i; + } + } + return -1; + } + + private void move(boolean up) { + if (listItems.isEmpty()) return; + int index = getSelectedIndex(); + int size = listItems.size(); + if (index == -1) { + if (up) { + index = listItems.size() - 1; + } else { + index = 0; + } + } else { + if (up) { + index = (index == 0) ? size - 1 : index - 1; + } else { + index = (index == size - 1) ? 0 : index + 1; + } + } + setSelected(index); + } + + private void showRecentResult() { + List<Item> recentSearch = getRecentSearch(false); + List<Item> favoriteSearch = getRecentSearch(true); + panelResult.removeAll(); + listItems.clear(); + if (recentSearch != null && !recentSearch.isEmpty()) { + panelResult.add(createLabel(Translations.get(TKey.SEARCH_RECENT))); + for (Item item : recentSearch) { + checkComponentOrientation(item); + panelResult.add(item); + listItems.add(item); + } + } + + if (favoriteSearch != null && !favoriteSearch.isEmpty()) { + panelResult.add(createLabel(Translations.get(TKey.SEARCH_FAVORITE))); + for (Item item : favoriteSearch) { + checkComponentOrientation(item); + panelResult.add(item); + listItems.add(item); + } + } + if (listItems.isEmpty()) { + panelResult.add(new NoRecentResult()); + } else { + setSelected(0); + } + updateLayout(); + } + + private JLabel createLabel(String title) { + JLabel label = new JLabel(title); + label.putClientProperty(FlatClientProperties.STYLE, "" + + "font:bold +1;" + + "border:5,15,5,15;"); + checkComponentOrientation(label); + return label; + } + + private List<Item> getRecentSearch(boolean favorite) { + String[] recentSearch = AppPreferences.getRecentSearch(favorite); + if (recentSearch == null) { + return null; + } + List<Item> list = new ArrayList<>(); + for (String s : recentSearch) { + Class<? extends Form> classForm = getClassForm(s); + if (MyMenuValidation.validation(classForm)) { + Item item = createRecentItem(s, favorite); + if (item != null) { + list.add(item); + } + } + } + return list; + } + + private Class<? extends Form> getClassForm(String name) { + for (Map.Entry<SystemForm, Class<? extends Form>> entry : formsMap.entrySet()) { + if (entry.getKey().name().equals(name)) { + return entry.getValue(); + } + } + return null; + } + + private Item createRecentItem(String name, boolean favorite) { + for (Map.Entry<SystemForm, Class<? extends Form>> entry : formsMap.entrySet()) { + if (entry.getKey().name().equals(name)) { + return new Item(entry.getKey(), entry.getValue(), true, favorite); + } + } + return null; + } + + private Component createNoResult(String text) { + JPanel panel = new JPanel(new MigLayout("insets 15 5 15 5,al center,gapx 1")); + JLabel label = new JLabel(Translations.get(TKey.SEARCH_NO_RECENT) + " \""); + JLabel labelEnd = new JLabel("\""); + label.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;"); + labelEnd.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;"); + JLabel labelText = new JLabel(text); + + panel.add(label); + panel.add(labelText); + panel.add(labelEnd); + return panel; + } + + public void clearSearch() { + if (!textSearch.getText().isEmpty()) { + textSearch.setText(""); + } else { + showRecentResult(); + } + } + + public void searchGrabFocus() { + textSearch.grabFocus(); + } + + private void checkComponentOrientation(Component com) { + if (getComponentOrientation().isLeftToRight() != com.getComponentOrientation().isLeftToRight()) { + com.applyComponentOrientation(getComponentOrientation()); + } + } + + private JTextField textSearch; + private JPanel panelResult; + + private static class NoRecentResult extends JPanel { + + public NoRecentResult() { + init(); + } + + private void init() { + setLayout(new MigLayout("insets 15 5 15 5,al center")); + JLabel label = new JLabel(Translations.get(TKey.SEARCH_NO_RECENT)); + label.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;" + + "font:bold;"); + add(label); + } + } + + private class Item extends JButton { + + private final SystemForm data; + private final Class<? extends Form> form; + private final boolean isRecent; + private final boolean isFavorite; + private Component itemSource; + + public Item(SystemForm data, Class<? extends Form> form, boolean isRecent, boolean isFavorite) { + this.data = data; + this.form = form; + this.isRecent = isRecent; + this.isFavorite = isFavorite; + init(); + } + + private void init() { + setFocusable(false); + setHorizontalAlignment(JButton.LEADING); + setLayout(new MigLayout("insets 3 3 3 0,filly,gapy 2", "[]push[]")); + putClientProperty(FlatClientProperties.STYLE, "" + + "background:null;" + + "arc:10;" + + "borderWidth:0;" + + "focusWidth:0;" + + "innerFocusWidth:0;" + + "[light]selectedBackground:lighten($Button.selectedBackground,9%)"); + JLabel labelDescription = new JLabel(data.description()); + labelDescription.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;"); + add(new JLabel(data.name()), "cell 0 0"); + add(labelDescription, "cell 0 1"); + if (!isRecent) { + add(new JLabel(new FlatMenuArrowIcon()), "cell 1 0,span 1 2"); + } else { + add(createRecentOption(), "cell 1 0,span 1 2"); + } + addActionListener(e -> { + if (itemSource == null) { + clearSelected(); + setSelected(true); + showForm(); + } else if (itemSource.getName().equals("remove")) { + removeRecent(); + } else if (itemSource.getName().equals("favorite")) { + addFavorite(); + } + }); + } + + private void clearSelected() { + for (Component com : getParent().getComponents()) { + if (com instanceof JButton jButton) { + jButton.setSelected(false); + } + } + } + + protected void showForm() { + ModalDialog.closeModal(FormSearch.ID); + Drawer.setSelectedItemClass(form); + if (!isFavorite) { + AppPreferences.addRecentSearch(data.name(), false); + } + } + + protected Component createRecentOption() { + JPanel panel = new JPanel(new MigLayout("insets n 0 n 0,fill,gapx 2", "", "[fill]")); + panel.setOpaque(false); + JButton cmdRemove = createButton("remove", "clear.svg", 0.35f, "Label.foreground", 0.9f); + if (!isFavorite) { + JButton cmdFavorite = createButton("favorite", "favorite.svg", 0.4f, "Component.accentColor", 0.9f); + panel.add(cmdFavorite); + } else { + JLabel label = new JLabel(new SVGIconUIColor("icons/favorite_filled.svg", 0.4f, "Component.accentColor", 0.8f)); + label.putClientProperty(FlatClientProperties.STYLE, "" + + "border:3,3,3,3;"); + panel.add(label); + } + panel.add(new JSeparator(JSeparator.VERTICAL), "gapy 5 5"); + panel.add(cmdRemove); + return panel; + } + + private JButton createButton(String name, String icon, float scale, String hoverKey, float alpha) { + SVGIconUIColor svgIcon = new SVGIconUIColor("icons/" + icon, scale, "Label.disabledForeground", alpha); + JButton button = new JButton(svgIcon); + button.setName(name); + button.setFocusable(false); + button.setContentAreaFilled(false); + button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + button.setModel(getModel()); + button.putClientProperty(FlatClientProperties.STYLE, "" + + "margin:3,3,3,3;"); + + button.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + svgIcon.setColorKey(hoverKey); + itemSource = (Component) e.getSource(); + } + + @Override + public void mouseExited(MouseEvent e) { + svgIcon.setColorKey("Label.disabledForeground"); + itemSource = null; + } + }); + return button; + } + + protected void removeRecent() { + AppPreferences.removeRecentSearch(data.name(), isFavorite); + panelResult.remove(this); + listItems.remove(this); + if (listItems.isEmpty()) { + panelResult.removeAll(); + panelResult.add(new NoRecentResult()); + } else { + if (getCount(isFavorite) == 0) { + if (isFavorite) { + panelResult.remove(panelResult.getComponentCount() - 1); + } else { + panelResult.remove(0); + } + } + } + updateLayout(); + } + + protected void addFavorite() { + AppPreferences.addRecentSearch(data.name(), true); + int[] index = getFirstFavoriteIndex(); + panelResult.remove(this); + listItems.remove(this); + Item item = new Item(data, form, isRecent, true); + checkComponentOrientation(item); + if (index == null) { + panelResult.add(createLabel(Translations.get(TKey.SEARCH_FAVORITE))); + panelResult.add(item); + listItems.add(item); + } else { + panelResult.remove(this); + listItems.remove(this); + panelResult.add(item, index[1] - 1); + listItems.add(index[0] - 1, item); + } + if (getCount(false) == 0) { + panelResult.remove(0); + } + updateLayout(); + } + + private int getCount(boolean favorite) { + int count = 0; + for (Item item : listItems) { + if (item.isFavorite == favorite) { + count++; + } + } + return count; + } + + private int[] getFirstFavoriteIndex() { + for (int i = 0; i < listItems.size(); i++) { + if (listItems.get(i).isFavorite) { + return new int[]{i, panelResult.getComponentZOrder(listItems.get(i))}; + } + } + return null; + } + } + + private static class PanelResult extends JPanel implements Scrollable { + + public PanelResult() { + super(new MigLayout("insets 3 10 3 10,fillx,wrap", "[fill]")); + } + + @Override + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(Rectangle rectangle, int i, int i1) { + return 50; + } + + @Override + public int getScrollableBlockIncrement(Rectangle rectangle, int i, int i1) { + return 50; + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + return false; + } + } +} diff --git a/src/main/java/backupmanager/gui/component/RefreshLine.java b/src/main/java/backupmanager/gui/component/RefreshLine.java new file mode 100644 index 00000000..cf5ebe26 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/RefreshLine.java @@ -0,0 +1,63 @@ +package backupmanager.gui.component; + +import java.awt.AlphaComposite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.RoundRectangle2D; + +import javax.swing.JPanel; +import javax.swing.UIManager; + +import com.formdev.flatlaf.util.Animator; +import com.formdev.flatlaf.util.CubicBezierEasing; +import com.formdev.flatlaf.util.UIScale; + +public class RefreshLine extends JPanel { + + private Animator animator; + private float animate; + + public RefreshLine() { + init(); + } + + private void init() { + animator = new Animator(500, new Animator.TimingTarget() { + @Override + public void timingEvent(float v) { + animate = v; + RefreshLine.this.repaint(); + } + + @Override + public void end() { + animate = 0f; + repaint(); + } + }); + animator.setInterpolator(CubicBezierEasing.EASE_OUT); + } + + public void refresh() { + if (animator.isRunning()) { + animator.stop(); + } + animate = 0f; + animator.start(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + float pad = UIScale.scale(5f); + float width = getWidth() - (pad * 2); + float height = getHeight(); + g2.setColor(UIManager.getColor("Component.accentColor")); + g2.setComposite(AlphaComposite.SrcOver.derive(0.5f)); + g2.fill(new RoundRectangle2D.Float(pad, 0, width * animate, height, height, height)); + g2.dispose(); + } +} diff --git a/src/main/java/backupmanager/gui/component/Subscription.java b/src/main/java/backupmanager/gui/component/Subscription.java new file mode 100644 index 00000000..397fda34 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/Subscription.java @@ -0,0 +1,116 @@ +package backupmanager.gui.component; + +import java.awt.Graphics; +import java.time.format.DateTimeFormatter; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JTextPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.text.DefaultCaret; + +import backupmanager.Enums.ConfigKey; +import backupmanager.Enums.SubscriptionStatus; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Helpers.SubscriptionHelper; +import backupmanager.Managers.WebsiteManager; +import backupmanager.gui.menu.DrawerManager; +import net.miginfocom.swing.MigLayout; + +public class Subscription extends JPanel { + + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + + public Subscription() { + init(); + } + + private void init() { + setLayout(new MigLayout("fillx, wrap, insets 25, width 520")); + setOpaque(false); + + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + String statusString = SubscriptionHelper.getSubscriptionStatusTranslated(status); + + backupmanager.Entities.Subscription subscription = SubscriptionHelper.getLastValidSubscription(); + String from = formatDate(subscription != null ? subscription.startDate() : null); + String to = formatDate(subscription != null ? subscription.endDate() : null); + + JTextPane description = createHtmlPane(buildHtml(status, statusString, from, to)); + + add(description, "growx"); + } + + private JTextPane createHtmlPane(String html) { + JTextPane pane = new JTextPane(); + pane.setContentType("text/html"); + pane.setText(html); + pane.setEditable(false); + pane.setOpaque(false); + pane.setBorder(BorderFactory.createEmptyBorder()); + + pane.setCaret(new DefaultCaret() { + @Override public void paint(Graphics g) {} + }); + + pane.addHyperlinkListener(e -> { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), e.getURL().toString()); + } + }); + + return pane; + } + + private String formatDate(java.time.temporal.TemporalAccessor date) { + if (date == null) return "N/A"; + return DATE_FORMAT.format(date); + } + + private String buildHtml(SubscriptionStatus status, String statusText, String validFrom, String validTo) { + String statusColor = switch (status) { + case ACTIVE -> "#2E7D32"; + case EXPIRATION -> "#ED6C02"; + case EXPIRED -> "#D32F2F"; + case NONE -> "#757575"; + }; + + String subject = encodeURIComponent("Support - Backup Manager"); + + return """ + <html> + <div> + <b>%s:</b> + <span style="color:%s;"><b>%s</b></span> + <br><br> + + <b>%s:</b> %s<br> + <b>%s:</b> %s<br> + + <br> + <a href="mailto:%s?subject=%s">%s</a> + %s + </div> + </html> + """.formatted( + Translations.get(TKey.SUBSCRIPTION_STATUS), + statusColor, + statusText, + Translations.get(TKey.SUBSCRIPTION_VALID_FROM), + validFrom, + Translations.get(TKey.SUBSCRIPTION_VALID_TO), + validTo, + ConfigKey.EMAIL.getValue(), + subject, + Translations.get(TKey.CONTACT_US), + Translations.get(TKey.SUBSCRIPTION_TO_EXTEND) + ); + } + + private String encodeURIComponent(String value) { + return java.net.URLEncoder + .encode(value, java.nio.charset.StandardCharsets.UTF_8) + .replace("+", "%20"); + } +} diff --git a/src/main/java/backupmanager/gui/component/ToolBarSelection.java b/src/main/java/backupmanager/gui/component/ToolBarSelection.java new file mode 100644 index 00000000..170c0fd4 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/ToolBarSelection.java @@ -0,0 +1,31 @@ +package backupmanager.gui.component; + +import java.util.function.Consumer; + +import javax.swing.ButtonGroup; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; + +import com.formdev.flatlaf.FlatClientProperties; + +public class ToolBarSelection<T> extends JToolBar { + + public ToolBarSelection(T[] data, Consumer<T> callBack) { + putClientProperty(FlatClientProperties.STYLE, "" + + "background:null;"); + ButtonGroup group = new ButtonGroup(); + boolean selected = false; + for (T d : data) { + JToggleButton button = new JToggleButton(d.toString()); + button.addActionListener(e -> callBack.accept(d)); + group.add(button); + add(button); + if (!selected) { + button.setSelected(true); + selected = true; + } + button.putClientProperty(FlatClientProperties.STYLE, "" + + "toolbar.margin:2,5,2,5;"); + } + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/BarChart.java b/src/main/java/backupmanager/gui/component/chart/BarChart.java new file mode 100644 index 00000000..071330a2 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/BarChart.java @@ -0,0 +1,82 @@ +package backupmanager.gui.component.chart; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.Axis; +import org.jfree.chart.axis.CategoryAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.labels.StandardCategoryToolTipGenerator; +import org.jfree.chart.plot.CategoryPlot; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.renderer.category.BarRenderer; +import org.jfree.data.category.CategoryDataset; +import org.jfree.data.category.DefaultCategoryDataset; + +import backupmanager.gui.component.chart.renderer.bar.ChartBarRenderer; +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; + +public class BarChart extends DefaultChartPanel { + + private BarRenderer renderer; + + @Override + protected JFreeChart createChart() { + return ChartFactory.createBarChart(null, null, null, new DefaultCategoryDataset()); + } + + @Override + protected void defaultStyleChart(JFreeChart chart, ChartPanel panel) { + + CategoryPlot plot = chart.getCategoryPlot(); + + NumberAxis range = (NumberAxis) plot.getRangeAxis(); + CategoryAxis domain = plot.getDomainAxis(); + + range.setAxisLineVisible(false); + domain.setAxisLineVisible(false); + + range.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); + range.setAutoRangeIncludesZero(true); + + // renderer + plot.setRenderer(getDefaultRenderer()); + } + + @Override + protected void styleChart(JFreeChart chart, ChartPanel panel) { + + CategoryPlot plot = chart.getCategoryPlot(); + + NumberAxis range = (NumberAxis) plot.getRangeAxis(); + CategoryAxis domain = plot.getDomainAxis(); + + range.setTickLabelInsets(ChartDrawingSupplier.scaleRectangleInsets(Axis.DEFAULT_TICK_LABEL_INSETS)); + domain.setTickLabelInsets(ChartDrawingSupplier.scaleRectangleInsets(Axis.DEFAULT_TICK_LABEL_INSETS)); + plot.setRangeGridlineStroke(ChartDrawingSupplier.getDefaultGridlineStroke()); + plot.setInsets(ChartDrawingSupplier.scaleRectangleInsets(Plot.DEFAULT_INSETS)); + } + + private BarRenderer getDefaultRenderer() { + + if (renderer == null) { + + renderer = new ChartBarRenderer(); + + // tooltip hover + renderer.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator()); + + // outline + renderer.setDrawBarOutline(false); + + // spacing + renderer.setItemMargin(0.1); + } + + return renderer; + } + + public void setDataset(CategoryDataset dataset) { + freeChart.getCategoryPlot().setDataset(dataset); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/CandlestickChart.java b/src/main/java/backupmanager/gui/component/chart/CandlestickChart.java new file mode 100644 index 00000000..9e5f63e7 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/CandlestickChart.java @@ -0,0 +1,159 @@ +package backupmanager.gui.component.chart; + +import java.awt.geom.Rectangle2D; +import java.text.DateFormat; +import java.text.NumberFormat; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartMouseEvent; +import org.jfree.chart.ChartMouseListener; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.Axis; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.labels.StandardCrosshairLabelGenerator; +import org.jfree.chart.panel.CrosshairOverlay; +import org.jfree.chart.plot.Crosshair; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.ui.RectangleAnchor; +import org.jfree.data.xy.OHLCDataset; + +import backupmanager.gui.component.chart.renderer.other.ChartCandlestickRenderer; +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; +import backupmanager.gui.component.chart.utils.CustomCrosshairToolTip; +import backupmanager.gui.component.chart.utils.DateCrosshairLabelGenerator; + +public class CandlestickChart extends DefaultChartPanel { + + private CustomCrosshairToolTip xCrosshair; + private CustomCrosshairToolTip yCrosshair; + + @Override + protected JFreeChart createChart() { + JFreeChart freeChart = ChartFactory.createCandlestickChart(null, null, null, null, false); + return freeChart; + } + + @Override + protected void defaultStyleChart(JFreeChart chart, ChartPanel panel) { + XYPlot plot = chart.getXYPlot(); + NumberAxis range = (NumberAxis) plot.getRangeAxis(); + DateAxis domain = (DateAxis) plot.getDomainAxis(); + + range.setNumberFormatOverride(NumberFormat.getCurrencyInstance()); + range.setAxisLineVisible(false); + range.setTickMarksVisible(false); + range.setAutoRangeIncludesZero(false); + range.setLowerMargin(0.15); + + domain.setUpperMargin(0.1); + domain.setAxisLineVisible(false); + domain.setTickMarksVisible(false); + + plot.setDomainPannable(true); + plot.setRangeGridlinesVisible(false); + plot.setDomainGridlinesVisible(false); + plot.setRenderer(new ChartCandlestickRenderer()); + + panel.setMouseWheelEnabled(false); + panel.setRangeZoomable(false); + } + + @Override + protected void styleChart(JFreeChart chart, ChartPanel panel) { + XYPlot plot = chart.getXYPlot(); + NumberAxis range = (NumberAxis) plot.getRangeAxis(); + DateAxis domain = (DateAxis) plot.getDomainAxis(); + + range.setTickLabelInsets(ChartDrawingSupplier.scaleRectangleInsets(Axis.DEFAULT_TICK_LABEL_INSETS)); + domain.setTickLabelInsets(ChartDrawingSupplier.scaleRectangleInsets(Axis.DEFAULT_TICK_LABEL_INSETS)); + + plot.setDomainGridlineStroke(ChartDrawingSupplier.getDefaultGridlineStroke()); + plot.setInsets(ChartDrawingSupplier.scaleRectangleInsets(4, 8, 15, 8)); + if (xCrosshair != null) { + xCrosshair.installStyle(); + } + if (yCrosshair != null) { + yCrosshair.installStyle(); + } + } + + @Override + protected void createAnnotation(JFreeChart chart, ChartPanel panel) { + xCrosshair = new CustomCrosshairToolTip(); + yCrosshair = new CustomCrosshairToolTip(); + chartPanel.addOverlay(createCrosshair(chartPanel)); + } + + private CrosshairOverlay createCrosshair(ChartPanel panel) { + CrosshairOverlay crosshairOverlay = new CrosshairOverlay(); + xCrosshair.setLabelAnchor(RectangleAnchor.BOTTOM); + yCrosshair.setLabelAnchor(RectangleAnchor.RIGHT); + DateFormat dateFormat = DateFormat.getDateInstance(); + xCrosshair.setLabelGenerator(new DateCrosshairLabelGenerator("{0}", dateFormat)); + yCrosshair.setLabelGenerator(new StandardCrosshairLabelGenerator("{0}", NumberFormat.getCurrencyInstance())); + xCrosshair.setLabelVisible(true); + yCrosshair.setLabelVisible(true); + crosshairOverlay.addDomainCrosshair(xCrosshair); + crosshairOverlay.addRangeCrosshair(yCrosshair); + + panel.addChartMouseListener(new ChartMouseListener() { + + private void showCrosshair(Crosshair crosshair, boolean visible) { + if (crosshair.isVisible() != visible) { + crosshair.setVisible(visible); + } + } + + @Override + public void chartMouseClicked(ChartMouseEvent chartMouseEvent) { + } + + @Override + public void chartMouseMoved(ChartMouseEvent event) { + Rectangle2D dataArea = panel.getScreenDataArea(); + if (!dataArea.contains(event.getTrigger().getPoint())) { + showCrosshair(xCrosshair, false); + showCrosshair(yCrosshair, false); + return; + } + showCrosshair(xCrosshair, true); + showCrosshair(yCrosshair, true); + + JFreeChart chart = panel.getChart(); + XYPlot plot = (XYPlot) chart.getPlot(); + int seriesCount = plot.getSeriesCount(); + OHLCDataset dataset = (OHLCDataset) plot.getDataset(); + + double x = plot.getDomainAxis().java2DToValue(event.getTrigger().getX(), dataArea, plot.getDomainAxisEdge()); + double y = plot.getRangeAxis().java2DToValue(event.getTrigger().getY(), dataArea, plot.getRangeAxisEdge()); + + double minDistance = Double.MAX_VALUE; + double closestX = 0; + boolean found = false; + + for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) { + for (int itemIndex = 0; itemIndex < plot.getDataset().getItemCount(seriesIndex); itemIndex++) { + double dataX = dataset.getXValue(seriesIndex, itemIndex); + double distance = Math.abs(x - dataX); + if (distance < minDistance) { + minDistance = distance; + closestX = dataX; + found = true; + } + } + } + if (found) { + xCrosshair.setValue(closestX); + } + yCrosshair.setValue(y); + } + }); + return crosshairOverlay; + } + + public void setDataset(OHLCDataset dataset) { + freeChart.getXYPlot().setDataset(dataset); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/DefaultChartPanel.java b/src/main/java/backupmanager/gui/component/chart/DefaultChartPanel.java new file mode 100644 index 00000000..2e4c722a --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/DefaultChartPanel.java @@ -0,0 +1,85 @@ +package backupmanager.gui.component.chart; + +import java.awt.Color; + +import javax.swing.JPanel; +import javax.swing.UIManager; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.ui.RectangleInsets; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; +import backupmanager.gui.component.chart.themes.DefaultChartTheme; +import net.miginfocom.swing.MigLayout; + +public abstract class DefaultChartPanel extends JPanel { + + public JFreeChart getFreeChart() { + return freeChart; + } + + public ChartPanel getChartPanel() { + return chartPanel; + } + + protected JFreeChart freeChart; + protected ChartPanel chartPanel; + + public DefaultChartPanel() { + init(); + } + + private void init() { + setLayout(new MigLayout("wrap,fillx,gap 0", "[fill]")); + putClientProperty(FlatClientProperties.STYLE_CLASS, "dashboardBackground"); + + freeChart = createChart(); + chartPanel = new ChartPanel(freeChart); + chartPanel.putClientProperty(FlatClientProperties.STYLE, "" + + "background:null;"); + createAnnotation(freeChart, chartPanel); + defaultStyleChart(freeChart, chartPanel); + applyStyledChart(freeChart, chartPanel); + + add(chartPanel); + } + + private void applyStyledChart(JFreeChart chart, ChartPanel panel) { + Color selectionColor = UIManager.getColor("List.selectionBackground"); + + // themes + DefaultChartTheme.applyTheme(chart); + // panel + chartPanel.setPopupMenu(null); + chartPanel.setZoomFillPaint(ChartDrawingSupplier.alpha(selectionColor, 0.2f)); + + // legend + if (chart.getLegend() != null) { + RectangleInsets insets = ChartDrawingSupplier.scaleRectangleInsets(new RectangleInsets(2, 2, 2, 2)); + chart.getLegend().setItemLabelPadding(insets); + } + styleChart(chart, panel); + } + + protected abstract JFreeChart createChart(); + + protected void defaultStyleChart(JFreeChart chart, ChartPanel panel) { + } + + protected void styleChart(JFreeChart chart, ChartPanel panel) { + } + + protected void createAnnotation(JFreeChart chart, ChartPanel panel) { + } + + @Override + public void updateUI() { + super.updateUI(); + if (freeChart != null && chartPanel != null) { + applyStyledChart(freeChart, chartPanel); + } + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/PieChart.java b/src/main/java/backupmanager/gui/component/chart/PieChart.java new file mode 100644 index 00000000..674f8bde --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/PieChart.java @@ -0,0 +1,54 @@ +package backupmanager.gui.component.chart; + +import java.awt.BasicStroke; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.labels.StandardPieSectionLabelGenerator; +import org.jfree.chart.plot.PiePlot; +import org.jfree.chart.ui.RectangleInsets; +import org.jfree.data.general.PieDataset; + +import com.formdev.flatlaf.util.UIScale; + +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; + +public class PieChart extends DefaultChartPanel { + + public PieChart() { + } + + @Override + protected JFreeChart createChart() { + JFreeChart freeChart = ChartFactory.createPieChart(null, null, true, false, false); + return freeChart; + } + + @Override + protected void defaultStyleChart(JFreeChart chart, ChartPanel panel) { + PiePlot plot = (PiePlot) chart.getPlot(); + plot.setShadowPaint(null); + plot.setLabelShadowPaint(null); + plot.setLabelOutlinePaint(null); + plot.setLabelBackgroundPaint(null); + plot.setDefaultSectionOutlineStroke(new BasicStroke(0f)); + plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} {2}")); + + // test + plot.setExplodePercent("Tablet", 0.15f); + } + + @Override + protected void styleChart(JFreeChart chart, ChartPanel panel) { + PiePlot plot = (PiePlot) chart.getPlot(); + plot.setLegendItemShape(ChartDrawingSupplier.getDefaultShape()); + plot.setLabelLinkStroke(new BasicStroke(UIScale.scale(1f))); + plot.setLabelPadding(ChartDrawingSupplier.scaleRectangleInsets(new RectangleInsets(2, 2, 2, 2))); + } + + public void setDataset(PieDataset dataset) { + PiePlot plot = (PiePlot) freeChart.getPlot(); + plot.setDataset(dataset); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/SpiderChart.java b/src/main/java/backupmanager/gui/component/chart/SpiderChart.java new file mode 100644 index 00000000..4fe8df5a --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/SpiderChart.java @@ -0,0 +1,101 @@ +package backupmanager.gui.component.chart; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Paint; +import java.util.List; + +import javax.swing.UIManager; + +import org.jfree.chart.ChartMouseEvent; +import org.jfree.chart.ChartMouseListener; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.entity.LegendItemEntity; +import org.jfree.chart.plot.SpiderWebPlot; +import org.jfree.data.category.CategoryDataset; + +import com.formdev.flatlaf.util.UIScale; + +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; +import backupmanager.gui.component.chart.themes.DefaultChartTheme; + +public class SpiderChart extends DefaultChartPanel { + + public SpiderChart() { + } + + @Override + protected JFreeChart createChart() { + SpiderWebPlot plot = new SpiderWebPlot(); + JFreeChart freeChart = new JFreeChart(null, null, plot, true); + return freeChart; + } + + @Override + protected void defaultStyleChart(JFreeChart chart, ChartPanel panel) { + SpiderWebPlot plot = (SpiderWebPlot) chart.getPlot(); + + panel.addChartMouseListener(new ChartMouseListener() { + + @Override + public void chartMouseClicked(ChartMouseEvent evt) { + if (evt.getEntity() instanceof LegendItemEntity) { + LegendItemEntity entity = (LegendItemEntity) evt.getEntity(); + Comparable key = entity.getSeriesKey(); + setSectionKey(key); + } + } + + @Override + public void chartMouseMoved(ChartMouseEvent evt) { + } + + private void setSectionKey(Comparable key) { + List<Comparable> keys = plot.getDataset().getRowKeys(); + int index = 0; + for (Comparable k : keys) { + if (k == key) { + Paint paint = plot.getSeriesPaint(index); + if (paint instanceof Color) { + if (((Color) paint).getAlpha() == 255) { + plot.setSeriesPaint(index, ChartDrawingSupplier.alpha(paint, 0.3f)); + } else { + plot.setSeriesPaint(index, ChartDrawingSupplier.alpha(paint, 1f)); + } + } + return; + } + index++; + } + } + }); + } + + @Override + protected void styleChart(JFreeChart chart, ChartPanel panel) { + SpiderWebPlot plot = (SpiderWebPlot) chart.getPlot(); + + plot.setLegendItemShape(ChartDrawingSupplier.getDefaultShape()); + plot.setSeriesOutlineStroke(new BasicStroke(UIScale.scale(1f))); + plot.setAxisLineStroke(new BasicStroke(UIScale.scale(1f))); + + int index = 0; + for (Color color : DefaultChartTheme.getColors()) { + initSeriesStyle(plot, index++, color, index != 1); + } + } + + private void initSeriesStyle(SpiderWebPlot plot, int series, Color color, boolean alpha) { + Paint c = alpha ? ChartDrawingSupplier.alpha(color, 0.3f) : color; + plot.setLabelPaint(UIManager.getColor("Label.foreground")); + plot.setSeriesPaint(series, c); + plot.setSeriesOutlinePaint(series, c); + plot.setSeriesOutlineStroke(series, new BasicStroke(UIScale.scale(1f))); + } + + public void setDataset(CategoryDataset dataset) { + SpiderWebPlot plot = (SpiderWebPlot) freeChart.getPlot(); + plot.setDataset(dataset); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/TimeSeriesChart.java b/src/main/java/backupmanager/gui/component/chart/TimeSeriesChart.java new file mode 100644 index 00000000..5702913d --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/TimeSeriesChart.java @@ -0,0 +1,162 @@ +package backupmanager.gui.component.chart; + +import java.awt.Color; +import java.awt.Font; +import java.awt.geom.Rectangle2D; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Date; + +import javax.swing.UIManager; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartMouseEvent; +import org.jfree.chart.ChartMouseListener; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.Axis; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.data.xy.XYDataset; + +import backupmanager.gui.component.chart.renderer.ChartXYCurveRenderer; +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; +import backupmanager.gui.component.chart.themes.DefaultChartTheme; +import backupmanager.gui.component.chart.utils.MultiXYTextAnnotation; + +public class TimeSeriesChart extends DefaultChartPanel { + + private XYItemRenderer renderer; + + public TimeSeriesChart() { + super(); + } + + @Override + protected JFreeChart createChart() { + JFreeChart chart = ChartFactory.createTimeSeriesChart(null, null, null, null); + return chart; + } + + public void setRenderer(XYItemRenderer renderer) { + if (this.renderer != renderer) { + this.renderer = renderer; + XYPlot plot = (XYPlot) freeChart.getPlot(); + plot.setRenderer(renderer); + DefaultChartTheme.applyTheme(freeChart); + } + } + + @Override + protected void defaultStyleChart(JFreeChart chart, ChartPanel panel) { + XYPlot plot = (XYPlot) chart.getPlot(); + NumberAxis range = (NumberAxis) plot.getRangeAxis(); + DateAxis domain = (DateAxis) plot.getDomainAxis(); + + range.setNumberFormatOverride(new DecimalFormat("#0.00")); + range.setAxisLineVisible(false); + range.setTickMarksVisible(false); + range.setUpperMargin(0.2); + range.setLowerMargin(0.1); + + domain.setAxisLineVisible(false); + domain.setTickMarksVisible(false); + + plot.setDomainPannable(true); + plot.setRangeGridlinesVisible(false); + + plot.setRenderer(getDefaultRender()); + } + + @Override + protected void styleChart(JFreeChart chart, ChartPanel panel) { + XYPlot plot = (XYPlot) chart.getPlot(); + Color background = getBackground(); + Color foreground = getForeground(); + Font font = getFont(); + Color selectionColor = UIManager.getColor("List.selectionBackground"); + Color border = UIManager.getColor("Component.borderColor"); + + NumberAxis range = (NumberAxis) plot.getRangeAxis(); + DateAxis domain = (DateAxis) plot.getDomainAxis(); + + range.setTickLabelInsets(ChartDrawingSupplier.scaleRectangleInsets(Axis.DEFAULT_TICK_LABEL_INSETS)); + domain.setTickLabelInsets(ChartDrawingSupplier.scaleRectangleInsets(Axis.DEFAULT_TICK_LABEL_INSETS)); + + plot.setDomainGridlineStroke(ChartDrawingSupplier.getDefaultGridlineStroke()); + plot.setInsets(ChartDrawingSupplier.scaleRectangleInsets(4, 8, 15, 8)); + + // annotation + MultiXYTextAnnotation annotation = (MultiXYTextAnnotation) plot.getAnnotations().get(0); + annotation.setBackgroundPaint(ChartDrawingSupplier.alpha(background, 0.7f)); + annotation.setDefaultPaint(foreground); + annotation.setFont(font); + annotation.setOutlinePaint(border); + annotation.setTitleLinePain(border); + annotation.setGridLinePaint(selectionColor); + } + + + @Override + protected void createAnnotation(JFreeChart chart, ChartPanel chartPanel) { + XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); + MultiXYTextAnnotation annotation = new MultiXYTextAnnotation(); + + DateFormat titleFormat = DateFormat.getDateInstance(); + NumberFormat valueFormat = new DecimalFormat("#0.00"); + annotation.setTitleGenerator(xValue -> titleFormat.format(new Date((long) xValue))); + annotation.setNumberFormat(valueFormat); + + plot.addAnnotation(annotation); + chartPanel.addChartMouseListener(new ChartMouseListener() { + @Override + public void chartMouseClicked(ChartMouseEvent event) { + } + + @Override + public void chartMouseMoved(ChartMouseEvent event) { + Rectangle2D dataArea = chartPanel.getScreenDataArea(); + if (!dataArea.contains(event.getTrigger().getPoint())) { + annotation.setLabels(null); + return; + } + double x = plot.getDomainAxis().java2DToValue(event.getTrigger().getX(), dataArea, plot.getDomainAxisEdge()); + XYDataset dataset = plot.getDataset(); + int seriesCount = plot.getSeriesCount(); + + double minDistance = Double.MAX_VALUE; + double closestX = 0; + boolean found = false; + + for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) { + for (int itemIndex = 0; itemIndex < plot.getDataset().getItemCount(seriesIndex); itemIndex++) { + double dataX = dataset.getXValue(seriesIndex, itemIndex); + double distance = Math.abs(x - dataX); + if (distance < minDistance) { + minDistance = distance; + closestX = dataX; + found = true; + } + } + } + if (found) { + annotation.autoCalculateX(closestX, dataset); + } + } + }); + } + + public XYItemRenderer getDefaultRender() { + if (renderer == null) { + renderer = new ChartXYCurveRenderer(); + } + return renderer; + } + + public void setDataset(XYDataset dataset) { + freeChart.getXYPlot().setDataset(dataset); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/ChartDeviationStepRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/ChartDeviationStepRenderer.java new file mode 100644 index 00000000..6b0292e9 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/ChartDeviationStepRenderer.java @@ -0,0 +1,25 @@ +package backupmanager.gui.component.chart.renderer; + +import java.awt.BasicStroke; + +import org.jfree.chart.renderer.xy.DeviationStepRenderer; + +import com.formdev.flatlaf.util.UIScale; + +public class ChartDeviationStepRenderer extends DeviationStepRenderer { + + public ChartDeviationStepRenderer() { + initStyle(); + } + + private void initStyle() { + setAutoPopulateSeriesOutlinePaint(true); + setDefaultOutlineStroke(new BasicStroke(UIScale.scale(6f))); + setUseOutlinePaint(true); + } + + @Override + public String toString() { + return "Deviation Step"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/ChartStackedXYBarRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/ChartStackedXYBarRenderer.java new file mode 100644 index 00000000..41deb528 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/ChartStackedXYBarRenderer.java @@ -0,0 +1,15 @@ +package backupmanager.gui.component.chart.renderer; + +import org.jfree.chart.renderer.xy.StackedXYBarRenderer; + +public class ChartStackedXYBarRenderer extends StackedXYBarRenderer { + + public ChartStackedXYBarRenderer() { + setMargin(0.3); + } + + @Override + public String toString() { + return "Stacked Bar"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYBarRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYBarRenderer.java new file mode 100644 index 00000000..4ab9ebc8 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYBarRenderer.java @@ -0,0 +1,19 @@ +package backupmanager.gui.component.chart.renderer; + +import org.jfree.chart.renderer.xy.ClusteredXYBarRenderer; + +import backupmanager.gui.component.chart.themes.DefaultChartTheme; + +public class ChartXYBarRenderer extends ClusteredXYBarRenderer { + + public ChartXYBarRenderer() { + setBarPainter(DefaultChartTheme.getInstance().getXYBarPainter()); + setShadowVisible(DefaultChartTheme.getInstance().isShadowVisible()); + setMargin(0.3); + } + + @Override + public String toString() { + return "Bar"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYCurveRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYCurveRenderer.java new file mode 100644 index 00000000..da1f2125 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYCurveRenderer.java @@ -0,0 +1,30 @@ +package backupmanager.gui.component.chart.renderer; + +import org.jfree.chart.renderer.xy.XYSplineRenderer; + +import com.formdev.flatlaf.util.UIScale; + +public class ChartXYCurveRenderer extends XYSplineRenderer { + + private static final int precision = 10; + + public ChartXYCurveRenderer() { + this(UIScale.scale(precision)); + } + + public ChartXYCurveRenderer(int precision) { + super(precision); + initStyle(); + } + + private void initStyle() { + setAutoPopulateSeriesOutlinePaint(true); + setAutoPopulateSeriesOutlineStroke(true); + setUseOutlinePaint(true); + } + + @Override + public String toString() { + return "Curve"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYDifferenceRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYDifferenceRenderer.java new file mode 100644 index 00000000..2833bdf3 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYDifferenceRenderer.java @@ -0,0 +1,22 @@ +package backupmanager.gui.component.chart.renderer; + +import java.awt.BasicStroke; + +import org.jfree.chart.renderer.xy.XYDifferenceRenderer; + +import backupmanager.gui.component.chart.themes.DefaultChartTheme; + +public class ChartXYDifferenceRenderer extends XYDifferenceRenderer { + + public ChartXYDifferenceRenderer() { + setPositivePaint(DefaultChartTheme.getColor(0)); + setNegativePaint(DefaultChartTheme.getColor(1)); + setAutoPopulateSeriesStroke(false); + setDefaultStroke(new BasicStroke(0f)); + } + + @Override + public String toString() { + return "Different"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYLineRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYLineRenderer.java new file mode 100644 index 00000000..53784c82 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/ChartXYLineRenderer.java @@ -0,0 +1,13 @@ +package backupmanager.gui.component.chart.renderer; + +public class ChartXYLineRenderer extends ChartXYCurveRenderer { + + public ChartXYLineRenderer() { + super(1); + } + + @Override + public String toString() { + return "Line"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/bar/ChartBarRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/bar/ChartBarRenderer.java new file mode 100644 index 00000000..e267cf28 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/bar/ChartBarRenderer.java @@ -0,0 +1,21 @@ +package backupmanager.gui.component.chart.renderer.bar; + +import org.jfree.chart.renderer.category.BarRenderer; + +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; + +public class ChartBarRenderer extends BarRenderer { + + public ChartBarRenderer() { + initStyle(); + } + + private void initStyle() { + setDefaultLegendShape(ChartDrawingSupplier.getDefaultShape()); + } + + @Override + public String toString() { + return "Bar"; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/renderer/other/ChartCandlestickRenderer.java b/src/main/java/backupmanager/gui/component/chart/renderer/other/ChartCandlestickRenderer.java new file mode 100644 index 00000000..75aae941 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/renderer/other/ChartCandlestickRenderer.java @@ -0,0 +1,36 @@ +package backupmanager.gui.component.chart.renderer.other; + +import java.awt.Color; +import java.awt.Paint; + +import org.jfree.chart.renderer.xy.CandlestickRenderer; +import org.jfree.data.xy.OHLCDataset; + +import com.formdev.flatlaf.util.UIScale; + +public class ChartCandlestickRenderer extends CandlestickRenderer { + + public ChartCandlestickRenderer() { + initRedGreenColor(this); + setCandleWidth(UIScale.scale(8)); + setDefaultToolTipGenerator(null); + } + + @Override + public Paint getItemPaint(int row, int column) { + OHLCDataset highLowData = (OHLCDataset) getPlot().getDataset(); + double yOpen = highLowData.getOpenValue(row, column); + double yClose = highLowData.getCloseValue(row, column); + boolean isUpCandle = yClose > yOpen; + if (isUpCandle) { + return getUpPaint(); + } else { + return getDownPaint(); + } + } + + public static void initRedGreenColor(CandlestickRenderer renderer) { + renderer.setDownPaint(new Color(241, 89, 89)); + renderer.setUpPaint(new Color(37, 176, 127)); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/themes/ChartDrawingSupplier.java b/src/main/java/backupmanager/gui/component/chart/themes/ChartDrawingSupplier.java new file mode 100644 index 00000000..9d119166 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/themes/ChartDrawingSupplier.java @@ -0,0 +1,94 @@ +package backupmanager.gui.component.chart.themes; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; + +import org.jfree.chart.plot.DefaultDrawingSupplier; +import org.jfree.chart.ui.RectangleInsets; + +import com.formdev.flatlaf.util.UIScale; + +public class ChartDrawingSupplier extends DefaultDrawingSupplier { + + protected ChartDrawingSupplier(ColorThemes colorThemes) { + this(colorThemes.getColors()); + } + + private ChartDrawingSupplier(Paint[] paintSequence) { + super(paintSequence, DEFAULT_FILL_PAINT_SEQUENCE, paintSequence, DEFAULT_STROKE_SEQUENCE, DEFAULT_OUTLINE_STROKE_SEQUENCE, DEFAULT_SHAPE_SEQUENCE); + } + + @Override + public Shape getNextShape() { + float size = UIScale.scale(3f); + return new Ellipse2D.Double(-size, -size, size * 2, size * 2); + } + + @Override + public Paint getNextOutlinePaint() { + return alpha(super.getNextOutlinePaint(), 0.2f); + } + + @Override + public Stroke getNextOutlineStroke() { + return scale(new BasicStroke(6f)); + } + + @Override + public Stroke getNextStroke() { + return scale(super.getNextStroke()); + } + + public static Shape getDefaultShape() { + float size = UIScale.scale(10f); + return new Ellipse2D.Double(0, 0, size, size); + } + + public static Stroke getDefaultGridlineStroke() { + float dash[] = {UIScale.scale(4f)}; + return new BasicStroke(UIScale.scale(1f), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, UIScale.scale(5f), dash, 0f); + } + + public static Stroke scale(Stroke stroke) { + if (stroke instanceof BasicStroke) { + BasicStroke basicStroke = (BasicStroke) stroke; + float lineWidth = UIScale.scale(basicStroke.getLineWidth()); + if (lineWidth != basicStroke.getLineWidth()) { + return new BasicStroke(lineWidth, basicStroke.getEndCap(), basicStroke.getLineJoin(), basicStroke.getMiterLimit(), basicStroke.getDashArray(), basicStroke.getDashPhase()); + } + } + return stroke; + } + + public static Paint alpha(Paint paint, float alpha) { + if (paint instanceof Color) { + Color color = (Color) paint; + return new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) (255 * alpha)); + } + return paint; + } + + public static RectangleInsets scaleRectangleInsets(double top, double left, double bottom, double right) { + return new RectangleInsets(UIScale.scale((float) top), UIScale.scale((float) left), UIScale.scale((float) bottom), UIScale.scale((float) right)); + } + + public static RectangleInsets scaleRectangleInsets(RectangleInsets rec) { + return scaleRectangleInsets(rec.getTop(), rec.getLeft(), rec.getBottom(), rec.getRight()); + } + + public static Rectangle2D scaleRectangle(Rectangle2D rec) { + Rectangle r = rec.getBounds(); + return new Rectangle2D.Double(UIScale.scale((float) r.getX()), UIScale.scale((float) rec.getY()), UIScale.scale((float) rec.getWidth()), UIScale.scale((float) rec.getHeight())); + } + + public static Rectangle2D unscaleRectangle(Rectangle2D rec) { + Rectangle r = rec.getBounds(); + return new Rectangle2D.Double(UIScale.unscale((float) r.getX()), UIScale.unscale((float) rec.getY()), UIScale.unscale((float) rec.getWidth()), UIScale.unscale((float) rec.getHeight())); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/themes/ColorThemes.java b/src/main/java/backupmanager/gui/component/chart/themes/ColorThemes.java new file mode 100644 index 00000000..8f69c3ab --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/themes/ColorThemes.java @@ -0,0 +1,87 @@ +package backupmanager.gui.component.chart.themes; + +import java.awt.Color; + +public enum ColorThemes { + DEFAULT( + Color.decode("#fd7f6f"), + Color.decode("#7eb0d5"), + Color.decode("#b2e061"), + Color.decode("#bd7ebe"), + Color.decode("#ffb55a"), + Color.decode("#ffee65"), + Color.decode("#beb9db"), + Color.decode("#fdcce5"), + Color.decode("#8bd3c7") + ), + RETRO_METRO( + Color.decode("#ea5545"), + Color.decode("#f46a9b"), + Color.decode("#ef9b20"), + Color.decode("#edbf33"), + Color.decode("#ede15b"), + Color.decode("#bdcf32"), + Color.decode("#87bc45"), + Color.decode("#27aeef"), + Color.decode("#b33dc6") + ), + BLUE_TO_YELLOW( + Color.decode("#115f9a"), + Color.decode("#1984c5"), + Color.decode("#22a7f0"), + Color.decode("#48b5c4"), + Color.decode("#76c68f"), + Color.decode("#a6d75b"), + Color.decode("#c9e52f"), + Color.decode("#d0ee11"), + Color.decode("#d0f400") + ), + SALMON_TO_AQUA( + Color.decode("#e27c7c"), + Color.decode("#a86464"), + Color.decode("#6d4b4b"), + Color.decode("#503f3f"), + Color.decode("#333333"), + Color.decode("#3c4e4b"), + Color.decode("#466964"), + Color.decode("#599e94"), + Color.decode("#6cd4c5") + ), + LIGHT( + Color.decode("#ffffff"), + Color.decode("#f0f0f0"), + Color.decode("#d9d9d9"), + Color.decode("#b3b3b3"), + Color.decode("#999999"), + Color.decode("#808080"), + Color.decode("#666666"), + Color.decode("#333333"), + Color.decode("#000000") + ), + DARK( + Color.decode("#444444"), + Color.decode("#555555"), + Color.decode("#666666"), + Color.decode("#777777"), + Color.decode("#888888"), + Color.decode("#999999"), + Color.decode("#aaaaaa"), + Color.decode("#bbbbbb"), + Color.decode("#cccccc") + ); + + private Color[] colors; + + ColorThemes(Color... colors) { + this.colors = colors; + } + + public Color[] getColors() { + return colors; + } + + @Override + public String toString() { + return super.toString().toLowerCase(); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/themes/DefaultChartTheme.java b/src/main/java/backupmanager/gui/component/chart/themes/DefaultChartTheme.java new file mode 100644 index 00000000..2f23ca04 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/themes/DefaultChartTheme.java @@ -0,0 +1,171 @@ +package backupmanager.gui.component.chart.themes; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.geom.RectangularShape; + +import javax.swing.UIManager; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.StandardChartTheme; +import org.jfree.chart.plot.PiePlot; +import org.jfree.chart.plot.SpiderWebPlot; +import org.jfree.chart.renderer.AbstractRenderer; +import org.jfree.chart.renderer.category.BarRenderer; +import org.jfree.chart.renderer.category.StandardBarPainter; +import org.jfree.chart.renderer.xy.CandlestickRenderer; +import org.jfree.chart.renderer.xy.StandardXYBarPainter; +import org.jfree.chart.renderer.xy.XYDifferenceRenderer; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.chart.ui.RectangleEdge; + +import com.formdev.flatlaf.util.UIScale; + +public class DefaultChartTheme extends StandardChartTheme { + + public static DefaultChartTheme getInstance() { + return instance; + } + + private static DefaultChartTheme instance = new DefaultChartTheme(); + public ColorThemes colorThemes = ColorThemes.DEFAULT; + + private DefaultChartTheme() { + super("Default Themes", false); + init(); + } + + private void init() { + Color background = new Color(0, 0, 0, 0); + Color foreground = UIManager.getColor("Label.foreground"); + Color border = UIManager.getColor("Component.borderColor"); + Font font = UIManager.getFont("Label.font"); + + setDrawingSupplier(new ChartDrawingSupplier(colorThemes)); + // chart + setChartBackgroundPaint(background); + + // plot + setPlotBackgroundPaint(background); + setPlotOutlinePaint(background); + + // renderer + setDomainGridlinePaint(border); + setRangeGridlinePaint(border); + + setBarPainter(new AlphaBarPainter()); + setXYBarPainter(new StandardXYBarPainter()); + + // text + setRegularFont(font); + setTitlePaint(foreground); + setSubtitlePaint(foreground); + setTickLabelPaint(foreground); + setItemLabelPaint(foreground); + setLabelLinkPaint(border); + + // legend + setLegendBackgroundPaint(background); + setLegendItemPaint(foreground); + + // other + } + + public static boolean setChartColors(ColorThemes colorThemes) { + if (instance.colorThemes != colorThemes) { + instance.colorThemes = colorThemes; + instance.setDrawingSupplier(new ChartDrawingSupplier(colorThemes)); + return true; + } + return false; + } + + public static Color getColor(int index) { + Color[] colors = instance.colorThemes.getColors(); + if (index > colors.length - 1) { + return colors[colors.length - 1]; + } + return colors[index]; + } + + public static Color[] getColors() { + return instance.colorThemes.getColors(); + } + + public static void applyTheme(JFreeChart chart) { + instance.init(); + instance.apply(chart); + } + + @Override + protected void applyToSpiderWebPlot(SpiderWebPlot plot) { + Color border = UIManager.getColor("Component.borderColor"); + plot.setLabelFont(getRegularFont()); + plot.setAxisLinePaint(border); + int index = 0; + for (Color color : instance.colorThemes.getColors()) { + boolean alpha = false; + Paint olePaint = plot.getSeriesPaint(index); + if (olePaint instanceof Color) { + alpha = ((Color) olePaint).getAlpha() < 255; + } + Paint c = alpha ? ChartDrawingSupplier.alpha(color, 0.3f) : color; + plot.setSeriesPaint(index, c); + plot.setSeriesOutlinePaint(index, c); + plot.setSeriesOutlineStroke(index, new BasicStroke(UIScale.scale(1f))); + index++; + } + } + + @Override + protected void applyToXYItemRenderer(XYItemRenderer renderer) { + super.applyToXYItemRenderer(renderer); + if (renderer != null) { + if (renderer instanceof XYDifferenceRenderer) { + XYDifferenceRenderer r = (XYDifferenceRenderer) renderer; + if (r.getAutoPopulateSeriesPaint()) { + r.setPositivePaint(getColor(0)); + r.setNegativePaint(getColor(1)); + } + } else if (renderer instanceof CandlestickRenderer) { + CandlestickRenderer r = (CandlestickRenderer) renderer; + if (r.getAutoPopulateSeriesPaint()) { + r.setDownPaint(getColor(0)); + r.setUpPaint(getColor(1)); + } + } + } + } + + @Override + protected void applyToAbstractRenderer(AbstractRenderer renderer) { + super.applyToAbstractRenderer(renderer); + + // apply null to series paint to get the new series paint + if (renderer.getAutoPopulateSeriesOutlinePaint()) { + int index = 0; + while (renderer.getSeriesOutlinePaint(index) != null) { + renderer.setSeriesOutlinePaint(index, null); + index++; + } + } + } + + @Override + protected void applyToPiePlot(PiePlot plot) { + plot.setLabelPaint(getItemLabelPaint()); + super.applyToPiePlot(plot); + } + + public class AlphaBarPainter extends StandardBarPainter { + @Override + public void paintBar(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base) { + g2.setComposite(AlphaComposite.SrcOver.derive(0.8f)); + super.paintBar(g2, renderer, row, column, bar, base); + } + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/utils/CustomCrosshairToolTip.java b/src/main/java/backupmanager/gui/component/chart/utils/CustomCrosshairToolTip.java new file mode 100644 index 00000000..069133ca --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/utils/CustomCrosshairToolTip.java @@ -0,0 +1,35 @@ +package backupmanager.gui.component.chart.utils; + +import java.awt.Color; +import java.awt.Font; + +import javax.swing.UIManager; + +import org.jfree.chart.plot.Crosshair; + +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; + +public class CustomCrosshairToolTip extends Crosshair { + + public CustomCrosshairToolTip() { + init(); + } + + private void init() { + installStyle(); + } + + public void installStyle() { + Color background = UIManager.getColor("Panel.background"); + Color foreground = UIManager.getColor("Label.foreground"); + Color border = UIManager.getColor("Component.borderColor"); + Font font = UIManager.getFont("Label.font"); + setLabelBackgroundPaint(ChartDrawingSupplier.alpha(background, 0.7f)); + setLabelPaint(foreground); + setLabelOutlinePaint(border); + setLabelFont(font); + setPaint(ChartDrawingSupplier.alpha(foreground, 0.5f)); + setStroke(ChartDrawingSupplier.getDefaultGridlineStroke()); + // setLabelPadding(ChartDrawingSupplier.scaleRectangleInsets(2, 5, 2, 5)); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/utils/DateCrosshairLabelGenerator.java b/src/main/java/backupmanager/gui/component/chart/utils/DateCrosshairLabelGenerator.java new file mode 100644 index 00000000..6662f5eb --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/utils/DateCrosshairLabelGenerator.java @@ -0,0 +1,26 @@ +package backupmanager.gui.component.chart.utils; + +import java.io.Serializable; +import java.text.DateFormat; +import java.text.MessageFormat; + +import org.jfree.chart.labels.CrosshairLabelGenerator; +import org.jfree.chart.plot.Crosshair; + +public class DateCrosshairLabelGenerator implements CrosshairLabelGenerator, Serializable { + + private final String labelTemplate; + private final DateFormat format; + + public DateCrosshairLabelGenerator(String labelTemplate, DateFormat format) { + this.labelTemplate = labelTemplate; + this.format = format; + } + + @Override + public String generateLabel(Crosshair crosshair) { + Object[] v = new Object[]{this.format.format(crosshair.getValue())}; + String result = MessageFormat.format(this.labelTemplate, v); + return result; + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/utils/MultiXYTextAnnotation.java b/src/main/java/backupmanager/gui/component/chart/utils/MultiXYTextAnnotation.java new file mode 100644 index 00000000..74f6b7ba --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/utils/MultiXYTextAnnotation.java @@ -0,0 +1,554 @@ +package backupmanager.gui.component.chart.utils; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.text.NumberFormat; + +import org.jfree.chart.annotations.AbstractXYAnnotation; +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.text.TextUtils; +import org.jfree.chart.ui.RectangleEdge; +import org.jfree.chart.ui.RectangleInsets; +import org.jfree.chart.ui.TextAnchor; +import org.jfree.chart.util.Args; +import org.jfree.data.general.DatasetUtils; +import org.jfree.data.xy.XYDataset; + +import com.formdev.flatlaf.util.UIScale; + +import backupmanager.gui.component.chart.themes.ChartDrawingSupplier; + +public class MultiXYTextAnnotation extends AbstractXYAnnotation { + + + public Font getFont() { + return font; + } + + public void setFont(Font font) { + Args.nullNotPermitted(font, "font"); + this.font = font; + fireAnnotationChanged(); + } + + public Paint getPaint() { + return paint; + } + + public void setPaint(Paint paint) { + Args.nullNotPermitted(paint, "paint"); + this.paint = paint; + fireAnnotationChanged(); + } + + public Paint getBackgroundPaint() { + return backgroundPaint; + } + + public void setBackgroundPaint(Paint backgroundPaint) { + this.backgroundPaint = backgroundPaint; + fireAnnotationChanged(); + } + + public boolean isOutlineVisible() { + return outlineVisible; + } + + public void setOutlineVisible(boolean outlineVisible) { + this.outlineVisible = outlineVisible; + fireAnnotationChanged(); + } + + public Paint getOutlinePaint() { + return outlinePaint; + } + + public void setOutlinePaint(Paint paint) { + Args.nullNotPermitted(paint, "paint"); + this.outlinePaint = paint; + fireAnnotationChanged(); + } + + public Paint getGridLinePaint() { + return gridLinePaint; + } + + public void setGridLinePaint(Paint paint) { + Args.nullNotPermitted(paint, "paint"); + this.gridLinePaint = paint; + fireAnnotationChanged(); + } + + public Paint getTitlePaint() { + return titlePaint; + } + + public void setTitlePaint(Paint paint) { + Args.nullNotPermitted(paint, "paint"); + this.titlePaint = paint; + fireAnnotationChanged(); + } + + public Paint getValuePaint() { + return valuePaint; + } + + public void setValuePaint(Paint paint) { + Args.nullNotPermitted(paint, "paint"); + this.valuePaint = paint; + fireAnnotationChanged(); + } + + public Paint getTitleLinePain() { + return titleLinePain; + } + + public void setTitleLinePain(Paint paint) { + this.titleLinePain = paint; + fireAnnotationChanged(); + } + + public Stroke getOutlineStroke() { + return outlineStroke; + } + + public void setOutlineStroke(Stroke stroke) { + Args.nullNotPermitted(stroke, "stroke"); + this.outlineStroke = stroke; + fireAnnotationChanged(); + } + + public Stroke getTitleLineStroke() { + return titleLineStroke; + } + + public void setTitleLineStroke(Stroke stroke) { + Args.nullNotPermitted(stroke, "stroke"); + this.titleLineStroke = stroke; + fireAnnotationChanged(); + } + + public double getGap() { + return gap; + } + + public void setGap(double gap) { + this.gap = gap; + fireAnnotationChanged(); + } + + public double getVerticalTextGap() { + return verticalTextGap; + } + + public void setVerticalTextGap(double verticalTextGap) { + this.verticalTextGap = verticalTextGap; + fireAnnotationChanged(); + } + + public RectangleInsets getPadding() { + return padding; + } + + public void setPadding(RectangleInsets padding) { + Args.nullNotPermitted(padding, "padding"); + this.padding = padding; + fireAnnotationChanged(); + } + + public RectangleInsets getSeriesPadding() { + return seriesPadding; + } + + public void setSeriesPadding(RectangleInsets padding) { + Args.nullNotPermitted(padding, "padding"); + this.seriesPadding = padding; + fireAnnotationChanged(); + } + + public RectangleInsets getTitlePadding() { + return titlePadding; + } + + public void setTitlePadding(RectangleInsets padding) { + Args.nullNotPermitted(padding, "padding"); + this.titlePadding = padding; + fireAnnotationChanged(); + } + + public RectangleInsets getTitleLinePadding() { + return titleLinePadding; + } + + public void setTitleLinePadding(RectangleInsets padding) { + Args.nullNotPermitted(padding, "padding"); + this.titleLinePadding = padding; + fireAnnotationChanged(); + } + + public double getRound() { + return round; + } + + public void setRound(double round) { + this.round = round; + fireAnnotationChanged(); + } + + public double getSeriesSize() { + return seriesSize; + } + + public void setSeriesSize(double seriesSize) { + this.seriesSize = seriesSize; + fireAnnotationChanged(); + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + fireAnnotationChanged(); + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + fireAnnotationChanged(); + } + + public Label[] getLabels() { + return labels; + } + + public void setLabels(Label[] labels) { + if (this.labels != labels) { + this.labels = labels; + fireAnnotationChanged(); + } + } + + public NumberFormat getNumberFormat() { + return numberFormat; + } + + public void setNumberFormat(NumberFormat numberFormat) { + Args.nullNotPermitted(numberFormat, "numberFormat"); + this.numberFormat = numberFormat; + fireAnnotationChanged(); + } + + public TitleGenerator getTitleGenerator() { + return titleGenerator; + } + + public void setTitleGenerator(TitleGenerator titleGenerator) { + this.titleGenerator = titleGenerator; + fireAnnotationChanged(); + } + + public void setDefaultPaint(Paint paint) { + Args.nullNotPermitted(paint, "paint"); + this.paint = paint; + this.titlePaint = paint; + this.valuePaint = paint; + fireAnnotationChanged(); + } + + private Font font; + private Paint paint; + private Paint backgroundPaint; + private boolean outlineVisible; + private Paint outlinePaint; + private Paint gridLinePaint; + private Paint titlePaint; + private Paint valuePaint; + private Paint titleLinePain; + private Stroke outlineStroke; + private Stroke titleLineStroke; + private double gap; + private double verticalTextGap; + private RectangleInsets padding; + private RectangleInsets seriesPadding; + private RectangleInsets titlePadding; + private RectangleInsets titleLinePadding; + private double round; + private double seriesSize; + private double x; + private double y; + private Label[] labels; + private NumberFormat numberFormat; + private TitleGenerator titleGenerator; + + public MultiXYTextAnnotation() { + this.paint = XYTextAnnotation.DEFAULT_PAINT; + this.font = XYTextAnnotation.DEFAULT_FONT; + this.backgroundPaint = new Color(255, 255, 255, 200); + this.outlinePaint = new Color(190, 190, 190); + this.gridLinePaint = new Color(25, 104, 148); + this.titlePaint = XYTextAnnotation.DEFAULT_PAINT; + this.valuePaint = XYTextAnnotation.DEFAULT_PAINT; + this.titleLinePain = new Color(190, 190, 190); + this.outlineVisible = true; + this.numberFormat = NumberFormat.getNumberInstance(); + this.padding = new RectangleInsets(10, 10, 10, 10); + this.seriesPadding = new RectangleInsets(3, 2, 3, 5); + this.titlePadding = new RectangleInsets(0, 0, 8, 0); + this.titleLinePadding = new RectangleInsets(0, 0, 8, 0); + this.outlineStroke = new BasicStroke(0.5f); + this.titleLineStroke = new BasicStroke(0.5f); + this.round = 10; + this.seriesSize = 10; + this.gap = 10; + this.verticalTextGap = 5; + } + + public void autoCalculateX(double x, XYDataset dataset) { + if (this.x != x || labels == null) { + this.x = x; + createValues(dataset); + } + } + + private void createValues(XYDataset dataset) { + int seriesCount = dataset.getSeriesCount(); + Label[] labels = new Label[seriesCount]; + double value = DatasetUtils.findYValue(dataset, 0, this.x); + double closestY = value; + labels[0] = new Label(dataset.getSeriesKey(0).toString(), getNumberFormat().format(value)); + for (int i = 1; i < seriesCount; i++) { + double y = DatasetUtils.findYValue(dataset, i, this.x); + String v = getNumberFormat().format(y); + String t = dataset.getSeriesKey(i).toString(); + labels[i] = new Label(t, v); + closestY = Math.max(closestY, y); + } + y = closestY; + this.labels = labels; + fireAnnotationChanged(); + } + + @Override + public void draw(Graphics2D g, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { + Graphics2D g2 = (Graphics2D) g.create(); + if (labels != null) { + UIScale.scaleGraphics(g2); + PlotOrientation orientation = plot.getOrientation(); + RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(plot.getDomainAxisLocation(), orientation); + RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(plot.getRangeAxisLocation(), orientation); + + float anchorX = UIScale.unscale((float) domainAxis.valueToJava2D(this.getX(), dataArea, domainEdge)); + float anchorY = UIScale.unscale((float) rangeAxis.valueToJava2D(this.getY(), dataArea, rangeEdge)); + + if (orientation == PlotOrientation.HORIZONTAL) { + float tempAnchor = anchorX; + anchorX = anchorY; + anchorY = tempAnchor; + } + Font font = getFont(); + g2.setFont(font.deriveFont(UIScale.unscale((float) font.getSize()))); + + String title = getTitleGenerator() == null ? null : titleGenerator.getTitle(this.x); + Rectangle2D bgRec = getBackgroundRectangle(g2, anchorX, anchorY, title); + + // adjust annotation + int shadow = 5; + int space = 2; + Rectangle2D rec = ChartDrawingSupplier.scaleRectangleInsets(space, space, space + shadow, space + shadow).createInsetRectangle(dataArea); + + Rectangle2D bgRecScale = ChartDrawingSupplier.scaleRectangle(bgRec); + if (!rec.contains(bgRecScale)) { + double x = bgRecScale.getX(); + double y = bgRecScale.getY(); + double width = bgRecScale.getWidth(); + double height = bgRecScale.getHeight(); + if (x < rec.getX()) { + x = rec.getX(); + } else if (x + width > rec.getX() + rec.getWidth()) { + x = rec.getX() + rec.getWidth() - width; + } + if (y < rec.getY()) { + y = rec.getY(); + } else if (y + height > rec.getY() + rec.getHeight()) { + y = rec.getY() + rec.getHeight() - height; + } + bgRec = ChartDrawingSupplier.unscaleRectangle(new Rectangle2D.Double(x, y, width, height)); + } + Shape shape = round > 0 ? + new RoundRectangle2D.Double(bgRec.getX(), bgRec.getY(), bgRec.getWidth(), bgRec.getHeight(), round, round) : + bgRec; + + // draw domain grid line without scale ui + g.setStroke(plot.getDomainGridlineStroke()); + g.setPaint(getGridLinePaint()); + double gx = UIScale.scale(anchorX); + g.draw(new Line2D.Double(gx, dataArea.getY(), gx, dataArea.getY() + dataArea.getHeight())); + + if (getBackgroundPaint() != null) { + g2.setPaint(getBackgroundPaint()); + g2.fill(shape); + } + float x = (float) (bgRec.getX() + getSeriesSizeWidth() + getPadding().getLeft()); + float y = (float) (bgRec.getY() + getPadding().getTop()); + float x2 = (float) (bgRec.getX() + bgRec.getWidth() - getPadding().getRight()); + float seriesX = (float) (bgRec.getX() + getPadding().getLeft()); + + // draw title + if (title != null) { + y += getTitlePadding().getTop(); + float titleX = (float) (bgRec.getX() + getPadding().getLeft() + getTitlePadding().getLeft()); + y += drawTitle(g2, titleX, y, title); + y += getTitlePadding().getBottom(); + if (getTitleLinePain() != null) { + float titleLineWidth = getTitleLineStrokeWidth(); + g2.setStroke(getTitleLineStroke()); + g2.setPaint(getTitleLinePain()); + double lineWidth = bgRec.getWidth() - getTitleLinePadding().getLeft() - getTitleLinePadding().getRight(); + double lineX = bgRec.getX() + getTitleLinePadding().getLeft(); + y += getTitleLinePadding().getTop(); + g2.draw(new Line2D.Double(lineX, y + titleLineWidth / 2f, lineX + lineWidth, y + titleLineWidth / 2f)); + y += titleLineWidth + getTitleLinePadding().getBottom(); + } + } + + // draw label + for (int i = 0; i < labels.length; i++) { + Label label = labels[i]; + float size = (float) drawLabel(g2, x, y, x2, label); + drawSeries(g2, seriesX, y, size, plot.getRenderer().getSeriesPaint(i)); + y += size + getVerticalTextGap(); + } + if (isOutlineVisible()) { + g2.setStroke(getOutlineStroke()); + g2.setPaint(getOutlinePaint()); + g2.draw(shape); + } + String toolTip = getToolTipText(); + String url = getURL(); + if (toolTip != null || url != null) { + addEntity(info, shape, rendererIndex, toolTip, url); + } + } + g2.dispose(); + } + + protected double drawTitle(Graphics2D g2, float x, float y, String title) { + g2.setPaint(getTitlePaint()); + double textHeight = TextUtils.drawAlignedString(title, g2, x, y, TextAnchor.TOP_LEFT).getHeight(); + return textHeight; + } + + protected double drawLabel(Graphics2D g2, float x, float y, float x2, Label label) { + g2.setPaint(getPaint()); + double textHeight = TextUtils.drawAlignedString(label.getText(), g2, x, y, TextAnchor.TOP_LEFT).getHeight(); + g2.setPaint(getValuePaint()); + double valueHeight = TextUtils.drawAlignedString(label.getValue(), g2, x2, y, TextAnchor.TOP_RIGHT).getHeight(); + return Math.max(textHeight, valueHeight); + } + + protected void drawSeries(Graphics2D g2, float x, float y, float height, Paint paint) { + double size = Math.min(height - (getSeriesPadding().getTop() + getSeriesPadding().getBottom()), getSeriesSize()); + double lx = x + (getSeriesSize() - size) / 2f; + double ly = y + ((height - size) / 2); + g2.setPaint(paint); + g2.fill(new Ellipse2D.Double(lx, ly, size, size)); + } + + protected Rectangle2D getBackgroundRectangle(Graphics2D g2, float anchorX, float anchorY, String title) { + if (labels == null || labels.length == 0) return null; + + double textWidth = 0, valueWidth = 0, titleWidth = 0, minLineWidth = 0, totalHeight = 0; + FontMetrics fm = g2.getFontMetrics(); + for (int i = 0; i < labels.length; i++) { + Label label = labels[i]; + Rectangle2D textBounds = TextUtils.getTextBounds(label.getText(), g2, fm); + Rectangle2D valueBounds = TextUtils.getTextBounds(label.getValue(), g2, fm); + totalHeight += Math.max(textBounds.getHeight(), valueBounds.getHeight()); + textWidth = Math.max(textWidth, textBounds.getWidth()); + valueWidth = Math.max(valueWidth, valueBounds.getWidth()); + } + if (title != null) { + Rectangle2D titleBounds = TextUtils.getTextBounds(title, g2, fm); + titleWidth = titleBounds.getWidth() + getTitlePadding().getLeft() + getTitlePadding().getRight(); + totalHeight += titleBounds.getHeight(); + totalHeight += getTitlePadding().getTop() + getTitlePadding().getBottom(); + if (getTitleLinePain() != null) { + totalHeight += getTitleLineStrokeWidth() + getTitleLinePadding().getTop() + getTitleLinePadding().getBottom(); + minLineWidth = getTitleLinePadding().getLeft() + getTitleLinePadding().getRight(); + } + } + if (labels.length > 1) { + totalHeight += (getVerticalTextGap() * (labels.length - 1)); + } + totalHeight += getPadding().getTop() + getPadding().getBottom(); + + double totalLabelWidth = textWidth + valueWidth + getSeriesSizeWidth() + getGap(); + double totalWidth = Math.max(totalLabelWidth, Math.max(titleWidth, minLineWidth)) + getPadding().getLeft() + getPadding().getRight(); + double space = 10; + return new Rectangle2D.Double(anchorX - space, anchorY - totalHeight - space, totalWidth, totalHeight); + } + + private double getSeriesSizeWidth() { + return getSeriesSize() + getSeriesPadding().getLeft() + getSeriesPadding().getRight(); + } + + private float getTitleLineStrokeWidth() { + Stroke stroke = getTitleLineStroke(); + if (stroke instanceof BasicStroke) { + float lineSize = ((BasicStroke) stroke).getLineWidth(); + return lineSize; + } + return 0; + } + + public static class Label { + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Label(String text, String value) { + this.text = text; + this.value = value; + } + + private String text; + private String value; + } + + public interface TitleGenerator { + String getTitle(double xValue); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/utils/ToolBarCategoryOrientation.java b/src/main/java/backupmanager/gui/component/chart/utils/ToolBarCategoryOrientation.java new file mode 100644 index 00000000..d1911375 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/utils/ToolBarCategoryOrientation.java @@ -0,0 +1,15 @@ +package backupmanager.gui.component.chart.utils; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; + +import backupmanager.gui.component.ToolBarSelection; + +public class ToolBarCategoryOrientation extends ToolBarSelection<String> { + + public ToolBarCategoryOrientation(JFreeChart chart) { + super(new String[]{"Vertical", "Horizontal"}, orientation -> { + chart.getCategoryPlot().setOrientation(orientation == "Horizontal" ? PlotOrientation.HORIZONTAL : PlotOrientation.VERTICAL); + }); + } +} diff --git a/src/main/java/backupmanager/gui/component/chart/utils/ToolBarTimeSeriesChartRenderer.java b/src/main/java/backupmanager/gui/component/chart/utils/ToolBarTimeSeriesChartRenderer.java new file mode 100644 index 00000000..905e60c4 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/chart/utils/ToolBarTimeSeriesChartRenderer.java @@ -0,0 +1,31 @@ +package backupmanager.gui.component.chart.utils; + +import org.jfree.chart.renderer.xy.XYItemRenderer; + +import backupmanager.gui.component.ToolBarSelection; +import backupmanager.gui.component.chart.TimeSeriesChart; +import backupmanager.gui.component.chart.renderer.ChartDeviationStepRenderer; +import backupmanager.gui.component.chart.renderer.ChartStackedXYBarRenderer; +import backupmanager.gui.component.chart.renderer.ChartXYBarRenderer; +import backupmanager.gui.component.chart.renderer.ChartXYDifferenceRenderer; + +public class ToolBarTimeSeriesChartRenderer extends ToolBarSelection<XYItemRenderer> { + + public ToolBarTimeSeriesChartRenderer(TimeSeriesChart chart) { + super(getRenderers(), renderer -> { + chart.setRenderer(renderer); + }); + } + + private static XYItemRenderer[] getRenderers() { + XYItemRenderer[] renderers = new XYItemRenderer[]{ + // new ChartXYCurveRenderer(), + // new ChartXYLineRenderer(), + new ChartXYBarRenderer(), + new ChartStackedXYBarRenderer(), + new ChartDeviationStepRenderer(), + new ChartXYDifferenceRenderer() + }; + return renderers; + } +} diff --git a/src/main/java/backupmanager/gui/component/dashboard/CardBox.java b/src/main/java/backupmanager/gui/component/dashboard/CardBox.java new file mode 100644 index 00000000..96ee1058 --- /dev/null +++ b/src/main/java/backupmanager/gui/component/dashboard/CardBox.java @@ -0,0 +1,56 @@ +package backupmanager.gui.component.dashboard; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.Icon; +import javax.swing.JPanel; +import javax.swing.JSeparator; + +import com.formdev.flatlaf.FlatClientProperties; + +import net.miginfocom.swing.MigLayout; + +public class CardBox extends JPanel { + + private final List<CardItem> cardItems = new ArrayList<>(); + + public CardBox() { + init(); + } + + private void init() { + setLayout(new MigLayout("", "[fill]", "[fill]")); + putClientProperty(FlatClientProperties.STYLE_CLASS, "dashboardBackground"); + } + + private void createSeparator() { + add(new JSeparator(JSeparator.VERTICAL), "width 3!"); + } + + public void addCardItem(Icon icon, String title) { + CardItem cardItem = new CardItem(icon, title); + cardItems.add(cardItem); + if (cardItems.size() > 1) { + createSeparator(); + } + add(cardItem, "width 100%"); + } + + public void setValueAt(int index, String value, String tags, boolean up) { + cardItems.get(index).setValue(value, tags, up); + } + + public void setCardIconColor(int index, Color color) { + cardItems.get(index).setCardIconColor(color); + } + + public void setTitleTextAt(int index, String text) { + cardItems.get(index).setTitleText(text); + } + + public void setDescriptionTextAt(int index, String text) { + cardItems.get(index).setDescriptionText(text); + } +} diff --git a/src/main/java/backupmanager/gui/component/dashboard/CardItem.java b/src/main/java/backupmanager/gui/component/dashboard/CardItem.java new file mode 100644 index 00000000..46d68dff --- /dev/null +++ b/src/main/java/backupmanager/gui/component/dashboard/CardItem.java @@ -0,0 +1,79 @@ +package backupmanager.gui.component.dashboard; + +import java.awt.Color; + +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import net.miginfocom.swing.MigLayout; + +public class CardItem extends JPanel { + + public CardItem(Icon icon, String title) { + init(icon, title); + } + + private void init(Icon icon, String title) { + setLayout(new MigLayout("hidemode 3,wrap", "15 push[] 15 push")); + putClientProperty(FlatClientProperties.STYLE, "" + + "background:null;"); + + lbTitle = new JLabel(title, icon, JLabel.LEADING); + lbValue = new JLabel("0"); + lbDescription = new JLabel("description"); + lbTags = new JLabel("0%"); + + lbTitle.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;"); + lbValue.putClientProperty(FlatClientProperties.STYLE, "" + + "font:+8;"); + + lbDescription.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;"); + + lbTags.putClientProperty(FlatClientProperties.STYLE_CLASS, "greenBadge small"); + + add(lbTitle); + add(lbValue); + add(lbDescription, "split 2"); + add(lbTags); + } + + public void setValue(String value, String tags, boolean up) { + lbValue.setText(value); + + lbTags.setText(tags); + lbTags.putClientProperty( + FlatClientProperties.STYLE_CLASS, + (up ? "greenBadge" : "redBadge") + " small" + ); + + lbTags.setVisible(tags != null); + } + + + public void setCardIconColor(Color color) { + if (lbTitle.getIcon() != null && lbTitle.getIcon() instanceof FlatSVGIcon) { + FlatSVGIcon icon = (FlatSVGIcon) lbTitle.getIcon(); + icon.getColorFilter().setMapper(color1 -> color); + repaint(); + } + } + + public void setTitleText(String text) { + lbTitle.setText(text); + } + + public void setDescriptionText(String text) { + lbDescription.setText(text); + } + + private JLabel lbTitle; + private JLabel lbValue; + private JLabel lbDescription; + private JLabel lbTags; +} diff --git a/src/main/java/backupmanager/gui/forms/CustomForm.java b/src/main/java/backupmanager/gui/forms/CustomForm.java new file mode 100644 index 00000000..95d2bd93 --- /dev/null +++ b/src/main/java/backupmanager/gui/forms/CustomForm.java @@ -0,0 +1,66 @@ +package backupmanager.gui.forms; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextPane; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.Managers.LanguageManager; +import backupmanager.gui.system.Form; +import backupmanager.interfaces.ITranslatable; +import net.miginfocom.swing.MigLayout; + +public abstract class CustomForm extends Form implements ITranslatable { + + private JLabel lbTitle; + private JTextPane text; + + protected CustomForm() {} + + protected void build() { + init(); + LanguageManager.register(this); + setTranslations(); + } + + protected abstract void init(); + + @Override + public abstract void setTranslations(); + + @Override + public void formInit() { + loadData(); + } + + @Override + public void formRefresh() { + loadData(); + } + + protected abstract void loadData(); + + protected JPanel createInfo(String title, String description, int level) { + JPanel panel = new JPanel(new MigLayout("fillx,wrap", "[fill]")); + lbTitle = new JLabel(title); + text = new JTextPane(); + text.setText(description); + text.setEditable(false); + text.setBorder(BorderFactory.createEmptyBorder()); + lbTitle.putClientProperty(FlatClientProperties.STYLE, "" + + "font:bold +" + (4 - level)); + panel.add(lbTitle); + panel.add(text, "width 500"); + return panel; + } + + protected void editTitle(String title) { + lbTitle.setText(title); + } + + protected void editDescription(String description) { + text.setText(description); + } +} diff --git a/src/main/java/backupmanager/gui/forms/FormBackupDashboard.java b/src/main/java/backupmanager/gui/forms/FormBackupDashboard.java new file mode 100644 index 00000000..9196aefb --- /dev/null +++ b/src/main/java/backupmanager/gui/forms/FormBackupDashboard.java @@ -0,0 +1,286 @@ +package backupmanager.gui.forms; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.util.UIScale; + +import backupmanager.Entities.BackupAnalyticsSnapshot; +import backupmanager.Entities.BackupRequest; +import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Services.BackupAnalyticsService; +import backupmanager.Utils.SystemForm; +import backupmanager.database.Repositories.BackupConfigurationRepository; +import backupmanager.database.Repositories.BackupRequestRepository; +import backupmanager.gui.component.ToolBarSelection; +import backupmanager.gui.component.chart.BarChart; +import backupmanager.gui.component.chart.TimeSeriesChart; +import backupmanager.gui.component.chart.themes.ColorThemes; +import backupmanager.gui.component.chart.themes.DefaultChartTheme; +import backupmanager.gui.component.dashboard.CardBox; +import net.miginfocom.swing.MigLayout; + +@SystemForm(name = "Backup Dashboard", description = "Backup analytics dashboard", tags = {"backups", "dashboard"}) +public class FormBackupDashboard extends CustomForm { + + private static final int CARD_TOTAL_CONFIG = 0; + private static final int CARD_SUCCESS_RATE = 1; + private static final int CARD_DURATION = 2; + private static final int CARD_COMPRESSION = 3; + // private static final int CARD_DISK_USAGE = 4; + + public FormBackupDashboard() { + build(); + } + + @Override + protected void init() { + setLayout(new MigLayout("wrap,fill", "[fill]", "[grow 0][fill]")); + createTitle(); + createPanelLayout(); + createCard(); + // createDiskUsageChart(); + createExecutionsByMonthChart(); + createAvgDurationChart(); + } + + @Override + protected void loadData() { + List<ConfigurationBackup> configurations = BackupConfigurationRepository.getBackupList(); + List<BackupRequest> requests = BackupRequestRepository.getRequestBackups(); + BackupAnalyticsSnapshot snapshot = BackupAnalyticsService.buildSnapshot(requests); + + cardBox.setValueAt(CARD_TOTAL_CONFIG, + String.valueOf(configurations.size()), + "", + true); + + cardBox.setValueAt(CARD_SUCCESS_RATE, + String.valueOf(snapshot.totalRequests()), + String.format("%.2f%%", snapshot.successRate()), + true); + + cardBox.setValueAt(CARD_DURATION, + String.format("%.2f min", BackupAnalyticsService.convertAvgDurationinMinutes(snapshot)), + "", + true); + + cardBox.setValueAt(CARD_COMPRESSION, + String.format("%.1f%%", snapshot.avgCompressionRate() * 100), + "", + true); + + durationChart.setDataset(BackupAnalyticsService.buildDurationTrendDataset(snapshot.durationTrend(), Translations.get(TKey.DASHBOARD_CHART_AVG_DURATION))); + executionsChart.setDataset(BackupAnalyticsService.buildRequestsPerMonthDataset(requests, Translations.get(TKey.DASHBOARD_CHART_EXECUTIONS))); + } + + protected void createTitle() { + + JPanel panel = new JPanel(new MigLayout("fillx", "[]push[][]")); + + title = new JLabel("Backup Analytics Dashboard"); + title.putClientProperty(FlatClientProperties.STYLE, + "font:bold +3"); + + ToolBarSelection<ColorThemes> toolBarSelection = + new ToolBarSelection<>(ColorThemes.values(), colorThemes -> { + + if (DefaultChartTheme.setChartColors(colorThemes)) { + + DefaultChartTheme.applyTheme(durationChart.getFreeChart()); + DefaultChartTheme.applyTheme(executionsChart.getFreeChart()); + + cardBox.setCardIconColor(0, DefaultChartTheme.getColor(0)); + cardBox.setCardIconColor(1, DefaultChartTheme.getColor(1)); + cardBox.setCardIconColor(2, DefaultChartTheme.getColor(2)); + cardBox.setCardIconColor(3, DefaultChartTheme.getColor(3)); + } + }); + + panel.add(title); + panel.add(toolBarSelection); + add(panel); + } + + private void createPanelLayout() { + + panelLayout = new JPanel(new DashboardLayout()); + + JScrollPane scrollPane = new JScrollPane(panelLayout); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + scrollPane.setHorizontalScrollBarPolicy( + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + scrollPane.getVerticalScrollBar().setUnitIncrement(10); + + scrollPane.getVerticalScrollBar().putClientProperty( + FlatClientProperties.STYLE, + "width:5;" + + "trackArc:$ScrollBar.thumbArc;" + + "trackInsets:0,0,0,0;" + + "thumbInsets:0,0,0,0;"); + + add(scrollPane); + } + + + private void createCard() { + + JPanel panel = new JPanel(new MigLayout("fillx", "[fill]")); + + cardBox = new CardBox(); + + cardBox.addCardItem( + createIcon("icons/dashboard/database.svg", DefaultChartTheme.getColor(CARD_TOTAL_CONFIG)), + "Total Backup Configurations"); + + cardBox.addCardItem( + createIcon("icons/dashboard/run.svg", DefaultChartTheme.getColor(CARD_SUCCESS_RATE)), + "Total Backup Executions"); + + cardBox.addCardItem( + createIcon("icons/dashboard/duration.svg", DefaultChartTheme.getColor(CARD_DURATION)), + "Avg Backup Duration"); + + cardBox.addCardItem( + createIcon("icons/dashboard/rate.svg", DefaultChartTheme.getColor(CARD_COMPRESSION)), + "Compression Rate"); + + panel.add(cardBox); + panelLayout.add(panel); + } + + private JPanel createChartPanel(int height) { + return new JPanel(new MigLayout("gap 14,wrap,fillx", "[fill]", "[" + height + "]")); + } + + private void createAvgDurationChart() { + JPanel panel = createChartPanel(350); + durationChart = new TimeSeriesChart(); + panel.add(durationChart); + panelLayout.add(panel); + } + + private void createExecutionsByMonthChart() { + JPanel panel = createChartPanel(350); + executionsChart = new BarChart(); + panel.add(executionsChart); + panelLayout.add(panel); + } + + + private Icon createIcon(String icon, Color color) { + return new FlatSVGIcon(icon, 20, 20).setColorFilter(new FlatSVGIcon.ColorFilter(color1 -> color)); + } + + @Override + public void setTranslations() { + title.setText(Translations.get(TKey.DASHBOARD_TITLE)); + cardBox.setTitleTextAt(CARD_TOTAL_CONFIG, Translations.get(TKey.DASHBOARD_CARD_TOTAL_CONFIGURATIONS)); + cardBox.setTitleTextAt(CARD_SUCCESS_RATE, Translations.get(TKey.DASHBOARD_CARD_TOTAL_EXECUTIONS)); + cardBox.setTitleTextAt(CARD_DURATION, Translations.get(TKey.DASHBOARD_CARD_AVG_DURATION)); + cardBox.setTitleTextAt(CARD_COMPRESSION, Translations.get(TKey.DASHBOARD_CARD_COMPRESSION_RATE)); + cardBox.setDescriptionTextAt(CARD_TOTAL_CONFIG, ""); + cardBox.setDescriptionTextAt(CARD_SUCCESS_RATE, Translations.get(TKey.DASHBOARD_CARD_SUCCESS_RATE)); + cardBox.setDescriptionTextAt(CARD_DURATION, ""); + cardBox.setDescriptionTextAt(CARD_COMPRESSION, ""); + } + + private JLabel title; + + private JPanel panelLayout; + private CardBox cardBox; + + private TimeSeriesChart durationChart; + private BarChart executionsChart; + + private class DashboardLayout implements LayoutManager { + + private final int gap = 0; + + @Override + public void addLayoutComponent(String name, Component comp) {} + + @Override + public void removeLayoutComponent(Component comp) {} + + @Override + public Dimension preferredLayoutSize(Container parent) { + + synchronized (parent.getTreeLock()) { + + Insets insets = parent.getInsets(); + + int width = insets.left + insets.right; + int height = insets.top + insets.bottom; + + int g = UIScale.scale(gap); + + int count = parent.getComponentCount(); + + for (int i = 0; i < count; i++) { + Component com = parent.getComponent(i); + Dimension size = com.getPreferredSize(); + height += size.height; + } + + if (count > 1) { + height += (count - 1) * g; + } + + return new Dimension(width, height); + } + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(10, 10); + } + + @Override + public void layoutContainer(Container parent) { + + synchronized (parent.getTreeLock()) { + + Insets insets = parent.getInsets(); + + int x = insets.left; + int y = insets.top; + + int width = parent.getWidth() - + (insets.left + insets.right); + + int g = UIScale.scale(gap); + + int count = parent.getComponentCount(); + + for (int i = 0; i < count; i++) { + + Component com = parent.getComponent(i); + Dimension size = com.getPreferredSize(); + + com.setBounds(x, y, width, size.height); + + y += size.height + g; + } + } + } + } +} diff --git a/src/main/java/backupmanager/gui/forms/FormBackupTable.java b/src/main/java/backupmanager/gui/forms/FormBackupTable.java new file mode 100644 index 00000000..1047cf72 --- /dev/null +++ b/src/main/java/backupmanager/gui/forms/FormBackupTable.java @@ -0,0 +1,563 @@ +package backupmanager.gui.forms; + +import java.awt.Component; +import java.time.LocalDateTime; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Entities.TimeInterval; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Exceptions.BackupDeletionException; +import backupmanager.Helpers.BackupHelper; +import static backupmanager.Helpers.BackupHelper.formatter; +import backupmanager.Services.BackupObserver; +import backupmanager.Services.BackupService; +import backupmanager.Utils.SystemForm; +import backupmanager.Utils.ToastUtils; +import backupmanager.Utils.table.TableHeaderAlignment; +import backupmanager.gui.Table.BackupTable; +import backupmanager.gui.Table.BackupTableDataService; +import backupmanager.gui.frames.Controllers.BackupManagerController; +import backupmanager.gui.frames.Controllers.BackupPopupController; +import backupmanager.gui.svg.SVGButton; +import net.miginfocom.swing.MigLayout; + +@SystemForm(name = "Backup Configurations", description = "Backup configurations and management", tags = {"list", "backups"}) +public class FormBackupTable extends CustomForm { + + private static final Logger logger = LoggerFactory.getLogger(FormBackupTable.class); + + private BackupManagerController managerController; + private BackupTableDataService tableService; + private BackupObserver backupObserver; + private final int COL_LAST_RUN = 3; + private final int COL_AUTOMATIC = 4; + private final int COL_NEXT_RUN = 5; + + private List<ConfigurationBackup> backups; + + public FormBackupTable() { + build(); + } + + @Override + protected void init() { + setLayout(new MigLayout( + "fill,wrap", + "[fill]", + "[][grow 100,fill][grow 0]" + )); + add(createInfo("Title", "Description", 1)); + add(createBorder(createBasicTable()), "gapx 7 7, grow"); + add(createBorder(createDetails()), "gapx 7 7, hmin 150"); + } + + @Override + public void formInit() { + DefaultTableModel model = (DefaultTableModel) backupTable.getModel(); + model.setColumnIdentifiers(ConfigurationBackup.getCSVHeaderArray()); + + backupTable.createDefaultColumnsFromModel(); + + tableService = new BackupTableDataService(backupTable, formatter); + managerController = new BackupManagerController(new BackupService(), tableService); + + backupObserver = new BackupObserver(tableService, 2000); + backupObserver.start(); + + formRefresh(); + } + + @Override + public void formRefresh() { + backups = managerController.getAllBackups(); + loadData(); + } + + @Override + protected void loadData() { + if (backups == null) + return; + + DefaultTableModel model = (DefaultTableModel) backupTable.getModel(); + model.setRowCount(0); + + for (ConfigurationBackup backup : backups) { + model.addRow(backup.toTableRow()); + } + } + + private Component createBorder(Component component) { + JPanel panel = new JPanel(new MigLayout("fill,insets 7 0 7 0", "[fill]", "[fill]")); + panel.add(component); + return panel; + } + + private Component createBasicTable() { + JPanel panelTable = new JPanel(new MigLayout( + "fill,wrap,insets 10 10 10 10", + "[fill]", + "[][grow,fill]" + )); + + // create table model + BackupTable table = new BackupTable( + new DefaultTableModel() { + + @Override + public Class<?> getColumnClass(int columnIndex) { + if (columnIndex == COL_AUTOMATIC) + return Boolean.class; + if (columnIndex == COL_LAST_RUN || columnIndex == COL_NEXT_RUN) + return LocalDateTime.class; + if (columnIndex == 6) + return TimeInterval.class; + if (columnIndex == 7) + return Integer.class; + return String.class; + } + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }); + + table.putClientProperty(FlatClientProperties.STYLE, "rowHeight:34; showHorizontalLines:false;"); + + table.getSelectionModel().addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + int row = table.getSelectedRow(); + if (row != -1) { + String details = managerController.buildDetails(backups.get(row)); + txtDetails.setText(details); + } + } + }); + + table.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mouseClicked(java.awt.event.MouseEvent e) { + if (e.getClickCount() == 2 && table.getSelectedRow() != -1) { + int row = table.getSelectedRow(); + logger.debug("Double clicked row: " + row); + showEditModal(backups.get(row)); + } + } + }); + + table.getInputMap(JComponent.WHEN_FOCUSED).put( + KeyStroke.getKeyStroke("DELETE"), + "deleteRow"); + + table.getActionMap().put("deleteRow", new AbstractAction() { + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + int row = table.getSelectedRow(); + if (row != -1) { + logger.debug("Deleted row: " + row); + showDeleteConfirmation(); + } + } + }); + + // table scroll + JScrollPane scrollPane = new JScrollPane(table); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + // alignment table header + table.getTableHeader().setDefaultRenderer( + new TableHeaderAlignment(table) { + + @Override + protected int getAlignment(int column) { + return SwingConstants.LEFT; + } + }); + + // style + panelTable.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:10;" + + "background:$Table.background;"); + table.getTableHeader().putClientProperty(FlatClientProperties.STYLE, "" + + "height:30;" + + "hoverBackground:null;" + + "pressedBackground:null;" + + "separatorColor:$TableHeader.background;"); + table.putClientProperty(FlatClientProperties.STYLE, "" + + "rowHeight:30;" + + "showHorizontalLines:true;" + + "intercellSpacing:0,1;" + + "cellFocusColor:$TableHeader.hoverBackground;" + + "selectionBackground:$TableHeader.hoverBackground;" + + "selectionInactiveBackground:$TableHeader.hoverBackground;" + + "selectionForeground:$Table.foreground;"); + scrollPane.getVerticalScrollBar().putClientProperty(FlatClientProperties.STYLE, "" + + "trackArc:$ScrollBar.thumbArc;" + + "trackInsets:3,3,3,3;" + + "thumbInsets:3,3,3,3;" + + "background:$Table.background;"); + + // create title + panelTable.add(createHeaderAction()); + panelTable.add(scrollPane, "grow, pushy"); + + backupTable = table; + + setRenderer(); + + buildTablePopupMenu(); + + return panelTable; + } + + private void setRenderer() { + DefaultTableCellRenderer dateRenderer = new DefaultTableCellRenderer() { + @Override + protected void setValue(Object value) { + if (value instanceof LocalDateTime date) { + setText(date.format(formatter)); + } else { + super.setValue(value); + } + } + }; + + backupTable.setDefaultRenderer(LocalDateTime.class, dateRenderer); + + DefaultTableCellRenderer baseRenderer = new DefaultTableCellRenderer() { + + @Override + public void setHorizontalAlignment(int alignment) { + super.setHorizontalAlignment(SwingConstants.LEFT); + } + + @Override + protected void setValue(Object value) { + + if (value instanceof Boolean bool) { + setHorizontalAlignment(SwingConstants.CENTER); + setText(bool ? "✓" : ""); + return; + } + + if (value instanceof Integer || value instanceof Long) { + setHorizontalAlignment(SwingConstants.LEFT); + setText(String.valueOf(value)); + return; + } + + if (value instanceof LocalDateTime date) { + setHorizontalAlignment(SwingConstants.LEFT); + setText(date.format(formatter)); + return; + } + + super.setValue(value); + } + }; + + backupTable.setDefaultRenderer(Object.class, baseRenderer); + } + + private void buildTablePopupMenu() { + JPopupMenu popupMenu = new JPopupMenu(); + + itemEdit = new JMenuItem("Edit"); + itemDelete = new JMenuItem("Delete"); + itemDuplicate = new JMenuItem("Duplicate"); + itemRename = new JMenuItem("Rename"); + itemOpenTargetPath = new JMenuItem("Open target path"); + itemOpenDestinationPath = new JMenuItem("Open destination path"); + + itemBackup = new JMenu("Backup"); + itemRunSingleBackup = new JMenuItem("Run single backup"); + itemAutoBackup = new JCheckBoxMenuItem("Auto backup"); + itemInterruptBackup = new JMenuItem("Interrupt backup process"); + + itemCopyText = new JMenu("Copy text"); + itemCopyBackupName = new JMenuItem("Copy backup name"); + itemCopyTargetPath = new JMenuItem("Copy target path"); + itemCopyDestinationPath = new JMenuItem("Copy destination path"); + + popupMenu.add(itemEdit); + popupMenu.add(itemDelete); + popupMenu.add(itemDuplicate); + popupMenu.add(itemRename); + popupMenu.addSeparator(); + popupMenu.add(itemOpenTargetPath); + popupMenu.add(itemOpenDestinationPath); + popupMenu.addSeparator(); + popupMenu.add(itemBackup); + itemBackup.add(itemRunSingleBackup); + itemBackup.add(itemAutoBackup); + itemBackup.add(itemInterruptBackup); + popupMenu.addSeparator(); + popupMenu.add(itemCopyText); + itemCopyText.add(itemCopyBackupName); + itemCopyText.add(itemCopyTargetPath); + itemCopyText.add(itemCopyDestinationPath); + + itemEdit.addActionListener(e -> handleAction("EDIT", itemInterruptBackup, itemRunSingleBackup)); + itemDelete.addActionListener(e -> handleAction("DELETE", itemInterruptBackup, itemRunSingleBackup)); + itemDuplicate.addActionListener(e -> handleAction("DUPLICATE", itemInterruptBackup, itemRunSingleBackup)); + itemRename.addActionListener(e -> handleAction("RENAME", itemInterruptBackup, itemRunSingleBackup)); + itemOpenTargetPath.addActionListener(e -> handleAction("OPEN_TARGET", itemInterruptBackup, itemRunSingleBackup)); + itemOpenDestinationPath.addActionListener(e -> handleAction("OPEN_DEST", itemInterruptBackup, itemRunSingleBackup)); + itemRunSingleBackup.addActionListener(e -> handleAction("RUN_SINGLE", itemInterruptBackup, itemRunSingleBackup)); + itemAutoBackup.addActionListener(e -> handleToggle()); + itemInterruptBackup.addActionListener(e -> handleAction("INTERRUPT_BACKUP", itemInterruptBackup, itemRunSingleBackup)); + itemCopyBackupName.addActionListener(e -> handleAction("COPY_NAME", itemInterruptBackup, itemRunSingleBackup)); + itemCopyTargetPath.addActionListener(e -> handleAction("COPY_TARGET", itemInterruptBackup, itemRunSingleBackup)); + itemCopyDestinationPath.addActionListener(e -> handleAction("COPY_DEST", itemInterruptBackup, itemRunSingleBackup)); + + itemInterruptBackup.setEnabled(false); // Disable interrupt option by default + + backupTable.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mousePressed(java.awt.event.MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(e); + } + } + + @Override + public void mouseReleased(java.awt.event.MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(e); + } + } + + private void showPopup(java.awt.event.MouseEvent e) { + int row = backupTable.rowAtPoint(e.getPoint()); + if (row >= 0) { + backupTable.setRowSelectionInterval(row, row); + } + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + ConfigurationBackup backup = getBackupFromTableRow(row); + itemAutoBackup.setSelected(backup.isAutomatic()); + } + }); + } + + private void handleAction(String action, JMenuItem interruptBackupPopupItem, JMenuItem RunBackupPopupItem) { + int selectedRow = backupTable.getSelectedRow(); + if (selectedRow < 0) + return; + + ConfigurationBackup backup = getBackupFromTableRow(selectedRow); + switch (action) { + case "EDIT" -> showEditModal(backup); + case "DELETE" -> { + try { + boolean deleted = BackupHelper.deleteBackupWithConfirmition(backup); + if (deleted) + ToastUtils.showSuccess(this, Translations.get(TKey.TOAST_BACKUP_DELETED)); + } catch (BackupDeletionException ex) { + ToastUtils.showError(this, Translations.get(TKey.TOAST_BACKUP_DELETED_ERROR)); + } + } + case "DUPLICATE" -> BackupPopupController.popupItemDuplicateBackup(backup); + case "RENAME" -> BackupPopupController.popupItemRenameBackup(this, backups, backup); + case "OPEN_TARGET" -> BackupPopupController.popupItemOpenInitialPath(this, backup); + case "OPEN_DEST" -> BackupPopupController.popupItemOpenDestinationPath(this, backup); + case "RUN_SINGLE" -> BackupPopupController.popupItemRunBackup(backup, tableService, interruptBackupPopupItem, RunBackupPopupItem); + case "COPY_NAME" -> BackupPopupController.popupItemCopyBackupName(backup); + case "COPY_TARGET" -> BackupPopupController.popupItemCopyInitialPath(backup); + case "COPY_DEST" -> BackupPopupController.popupItemCopyDestinationPath(backup); + } + + formRefresh(); + } + + private void handleToggle() { + int selectedRow = backupTable.getSelectedRow(); + if (selectedRow < 0) + return; + + ConfigurationBackup backup = getBackupFromTableRow(selectedRow); + BackupPopupController.popupItemAutoBackup(this, backup); + + formRefresh(); + } + + private ConfigurationBackup getBackupFromTableRow(int row) { + row = backupTable.convertRowIndexToModel(row); + return backups.get(row); + } + + private Component createDetails() { + JPanel detailsPanel = new JPanel( + new MigLayout("fill,insets 5 0 5 0", "[fill]", "[grow]") + ); + + txtDetails = new JTextPane(); + txtDetails.setEditable(false); + txtDetails.setContentType("text/html"); + + JScrollPane detailScroll = new JScrollPane(txtDetails); + detailScroll.putClientProperty(FlatClientProperties.STYLE, + "arc:10; border:1,1,1,1,$Component.borderColor"); + + detailsPanel.add(detailScroll, "grow"); + return detailsPanel; + } + + private Component createHeaderAction() { + JPanel panel = new JPanel(new MigLayout("insets 5 20 5 20", "[fill,300]push[][]")); + + txtSearch = new JTextField(); + txtSearch.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, "Search..."); + txtSearch.putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_ICON, new FlatSVGIcon("icons/search.svg", 0.4f)); + cmdCreate = new SVGButton("Create"); + cmdEdit = new SVGButton("Edit"); + cmdDelete = new SVGButton("Delete"); + + cmdCreate.setSvgImage("icons/add.svg", 16, 16); + cmdEdit.setSvgImage("icons/edit.svg", 16, 16); + cmdDelete.setSvgImage("icons/delete.svg", 16, 16); + + cmdCreate.putClientProperty(FlatClientProperties.STYLE, "background:$Component.accentColor;"); + cmdDelete.putClientProperty(FlatClientProperties.STYLE, "background:$Component.error.background;"); + + cmdCreate.addActionListener(e -> showCreateModal()); + cmdEdit.addActionListener(e -> showEditModal()); + cmdDelete.addActionListener(e -> showDeleteConfirmation()); + panel.add(txtSearch); + panel.add(cmdCreate); + panel.add(cmdEdit); + panel.add(cmdDelete); + + txtSearch.getDocument().addDocumentListener(new DocumentListener() { + private void update() { + if (backups == null) + return; + + String research = txtSearch.getText(); + backups = managerController.getAllBackups(); + backups = managerController.researchInTableAndGet(backups, research); + loadData(); + } + + @Override + public void insertUpdate(DocumentEvent e) { update(); } + @Override + public void removeUpdate(DocumentEvent e) { update(); } + @Override + public void changedUpdate(DocumentEvent e) { update(); } + }); + + panel.putClientProperty(FlatClientProperties.STYLE, "" + + "background:null;"); + return panel; + } + + public void showCreateModal() { + managerController.showCreateModal(this); + } + + private void showEditModal(ConfigurationBackup backup) { + managerController.showEditModal(this, backup); + } + + private void showEditModal() { + int selectedRow = backupTable.getSelectedRow(); + if (selectedRow < 0) + return; + + ConfigurationBackup backup = getBackupFromTableRow(selectedRow); + managerController.showEditModal(this, backup); + loadData(); + } + + private void showDeleteConfirmation() { + int selectedRow = backupTable.getSelectedRow(); + if (selectedRow < 0) + return; + + ConfigurationBackup backup = getBackupFromTableRow(selectedRow); + try { + boolean deleted = BackupHelper.deleteBackupWithConfirmition(backup); + if (deleted) + ToastUtils.showSuccess(this, Translations.get(TKey.TOAST_BACKUP_DELETED)); + } catch (BackupDeletionException e) { + ToastUtils.showError(this, Translations.get(TKey.TOAST_BACKUP_DELETED_ERROR)); + } + formRefresh(); + } + + @Override + public void setTranslations() { + editTitle(Translations.get(TKey.BACKUP_LIST_TITLE)); + editDescription(Translations.get(TKey.BACKUP_LIST_DESCRIPTION)); + + txtSearch.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.RESEARCH_BAR_PLACEHOLDER)); + txtSearch.setToolTipText(Translations.get(TKey.RESEARCH_BAR_TOOLTIP)); + + cmdCreate.setText(Translations.get(TKey.CREATE_BUTTON)); + cmdEdit.setText(Translations.get(TKey.EDIT_BUTTON)); + cmdDelete.setText(Translations.get(TKey.DELETE_BUTTON)); + + itemEdit.setText(Translations.get(TKey.EDIT_POPUP)); + itemDelete.setText(Translations.get(TKey.DELETE_POPUP)); + itemDuplicate.setText(Translations.get(TKey.DUPLICATE_POPUP)); + itemRename.setText(Translations.get(TKey.RENAME_BACKUP_POPUP)); + itemOpenTargetPath.setText(Translations.get(TKey.OPEN_INITIAL_FOLDER_POPUP)); + itemOpenDestinationPath.setText(Translations.get(TKey.OPEN_DESTINATION_FOLDER_POPUP)); + itemBackup.setText(Translations.get(TKey.BACKUP_POPUP)); + itemRunSingleBackup.setText(Translations.get(TKey.SINGLE_BACKUP_POPUP)); + itemAutoBackup.setText(Translations.get(TKey.AUTO_BACKUP_POPUP)); + itemInterruptBackup.setText(Translations.get(TKey.INTERRUPT_POPUP)); + itemCopyText.setText(Translations.get(TKey.COPY_TEXT_POPUP)); + itemCopyBackupName.setText(Translations.get(TKey.COPY_BACKUP_NAME_POPUP)); + itemCopyTargetPath.setText(Translations.get(TKey.COPY_INITIAL_PATH_POPUP)); + itemCopyDestinationPath.setText(Translations.get(TKey.COPY_DESTINATION_PATH_BACKUP)); + } + + private BackupTable backupTable; + private JTextPane txtDetails; + private JTextField txtSearch; + private SVGButton cmdCreate; + private SVGButton cmdEdit; + private SVGButton cmdDelete; + + private JMenuItem itemEdit; + private JMenuItem itemDelete; + private JMenuItem itemDuplicate; + private JMenuItem itemRename; + private JMenuItem itemOpenTargetPath; + private JMenuItem itemOpenDestinationPath; + private JMenu itemBackup; + private JMenuItem itemRunSingleBackup; + private JCheckBoxMenuItem itemAutoBackup; + private JMenuItem itemInterruptBackup; + private JMenu itemCopyText; + private JMenuItem itemCopyBackupName; + private JMenuItem itemCopyTargetPath; + private JMenuItem itemCopyDestinationPath; +} diff --git a/src/main/java/backupmanager/gui/forms/FormHistory.java b/src/main/java/backupmanager/gui/forms/FormHistory.java new file mode 100644 index 00000000..5b8bde64 --- /dev/null +++ b/src/main/java/backupmanager/gui/forms/FormHistory.java @@ -0,0 +1,83 @@ +package backupmanager.gui.forms; + +import java.awt.Component; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.Enums.ConfigKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Utils.SystemForm; +import net.miginfocom.swing.MigLayout; + +@SystemForm(name = "History", description = "application history log") +public class FormHistory extends CustomForm { + public FormHistory() { + build(); + } + + @Override + protected void init() { + setLayout(new MigLayout("fill,wrap", "[fill]", "[][grow,fill]")); + add(createInfo("Information", "Here you can find the application logs, useful for troubleshooting and understanding the application's behavior over time.", 1)); + add(createLogPanel()); + } + + @Override + protected void loadData() { + try { + Path logFile = Paths.get( + System.getProperty("user.home"), + ".backupmanager", + "logs", + ConfigKey.LOG_FILE_STRING.getValue() + ); + + if (!Files.exists(logFile)) { + logsPane.setText("Log file not found:\n" + logFile); + return; + } + + String content = Files.readString(logFile); + + logsPane.setText(content); + logsPane.setCaretPosition(0); + + } catch (IOException ex) { + logsPane.setText("Failed to load logs:\n" + ex.getMessage()); + } + } + + private Component createLogPanel() { + JPanel panel = new JPanel( + new MigLayout("fill,insets 5 0 5 0", "[fill]", "[grow]") + ); + + logsPane = new JTextPane(); + logsPane.setEditable(false); + logsPane.setContentType("text/plain"); + + JScrollPane detailScroll = new JScrollPane(logsPane); + detailScroll.putClientProperty(FlatClientProperties.STYLE, + "arc:10; border:1,1,1,1,$Component.borderColor"); + + panel.add(detailScroll, "grow"); + return panel; + } + + @Override + public void setTranslations() { + editTitle(Translations.get(TKey.HISTORY_LOGS_TITLE)); + editDescription(Translations.get(TKey.HISTORY_LOGS_DESCRIPTION)); + } + + private JTextPane logsPane; +} diff --git a/src/main/java/backupmanager/gui/forms/FormLogin.java b/src/main/java/backupmanager/gui/forms/FormLogin.java new file mode 100644 index 00000000..0395c9b7 --- /dev/null +++ b/src/main/java/backupmanager/gui/forms/FormLogin.java @@ -0,0 +1,145 @@ +package backupmanager.gui.forms; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.Entities.User; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.LimitDocument; +import backupmanager.Services.LoginService; +import backupmanager.gui.Controllers.EntryUserController; +import backupmanager.gui.menu.DrawerManager; +import backupmanager.gui.system.FormManager; +import net.miginfocom.swing.MigLayout; + +public class FormLogin extends CustomForm { + + private final LoginService loginService = new LoginService(); + private final EntryUserController userController = new EntryUserController(); + + public FormLogin() { + build(); + } + + @Override + protected void init() { + setLayout(new MigLayout("al center center")); + + if (!loginService.isFirstAccess()) { + javax.swing.SwingUtilities.invokeLater(this::showMainForm); + return; + } + + loadData(); + } + + + @Override + protected void loadData() { + JPanel panelLogin = new JPanel(new MigLayout()); + JPanel loginContent = new JPanel( + new MigLayout("fillx,wrap,insets 35 35 25 35", "[fill,300]") + ); + + lbTitle = new JLabel("Login"); + lbDescription = new JLabel("Please enter your data to access the system"); + + lbTitle.putClientProperty(FlatClientProperties.STYLE, "font:bold +12;"); + + loginContent.add(lbTitle); + loginContent.add(lbDescription); + + txtName = new JTextField(); + txtSurname = new JTextField(); + txtEmail = new JTextField(); + labelName = new JLabel("Name"); + labelSurname = new JLabel("Surname"); + labelEmail = new JLabel("Email"); + + JButton cmdLogin = new JButton("Login"); + + panelLogin.putClientProperty(FlatClientProperties.STYLE, + "[light]border:5,5,5,5,shade($Panel.background,10%),,20;" + + "[dark]border:5,5,5,5,tint($Panel.background,5%),,20;" + + "[light]background:shade($Panel.background,3%);" + + "[dark]background:tint($Panel.background,2%);" + ); + + loginContent.putClientProperty(FlatClientProperties.STYLE, "background:null;"); + + String fieldStyle = "margin:4,10,4,10;arc:12;"; + + txtName.putClientProperty(FlatClientProperties.STYLE, fieldStyle); + txtSurname.putClientProperty(FlatClientProperties.STYLE, fieldStyle); + txtEmail.putClientProperty(FlatClientProperties.STYLE, fieldStyle); + cmdLogin.putClientProperty(FlatClientProperties.STYLE, fieldStyle); + + txtName.setDocument(new LimitDocument(20)); + txtSurname.setDocument(new LimitDocument(20)); + txtEmail.setDocument(new LimitDocument(32)); + + loginContent.add(labelName, "gapy 25"); + loginContent.add(txtName); + + loginContent.add(labelSurname, "gapy 10"); + loginContent.add(txtSurname); + + loginContent.add(labelEmail, "gapy 10"); + loginContent.add(txtEmail); + + loginContent.add(cmdLogin, "gapy 20"); + + panelLogin.add(loginContent); + add(panelLogin); + + cmdLogin.addActionListener(e -> { + + String name = txtName.getText().trim(); + String surname = txtSurname.getText().trim(); + String email = txtEmail.getText().trim(); + + if (!userController.isInputOkAndShowErrorIfNecessary(this, name, surname, email)) + return; + + User user = new User(name, surname, email); + loginService.createUserAndSendEmail(user); + + javax.swing.SwingUtilities.invokeLater(this::showMainForm); + }); + } + + private void showMainForm() { + DrawerManager.getInstance().getDrawer(); + FormManager.login(); + } + + @Override + public void setTranslations() { + if (lbTitle == null) { + return; + } + + lbTitle.setText(Translations.get(TKey.USER_TITLE)); + lbDescription.setText(Translations.get(TKey.USER_DESCRIPTION)); + txtName.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.USER_NAME_PLACEHOLDER)); + txtSurname.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.USER_SURNAME_PLACEHOLDER)); + txtEmail.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.USER_EMAIL_PLACEHOLDER)); + labelName.setText(Translations.get(TKey.USER_NAME)); + labelSurname.setText(Translations.get(TKey.USER_SURNAME)); + labelEmail.setText(Translations.get(TKey.USER_EMAIL)); + } + + private JTextField txtName; + private JTextField txtSurname; + private JTextField txtEmail; + private JLabel labelName; + private JLabel labelSurname; + private JLabel labelEmail; + private JLabel lbTitle; + private JLabel lbDescription; +} diff --git a/src/main/java/backupmanager/gui/forms/FormSetting.java b/src/main/java/backupmanager/gui/forms/FormSetting.java new file mode 100644 index 00000000..696de8fb --- /dev/null +++ b/src/main/java/backupmanager/gui/forms/FormSetting.java @@ -0,0 +1,549 @@ +package backupmanager.gui.forms; + +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.event.ActionEvent; +import java.lang.reflect.InvocationTargetException; + +import javax.swing.ButtonGroup; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTabbedPane; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.TitledBorder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.FlatDarculaLaf; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatIntelliJLaf; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.FlatLightLaf; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.themes.FlatMacDarkLaf; +import com.formdev.flatlaf.themes.FlatMacLightLaf; +import com.formdev.flatlaf.util.ScaledEmptyBorder; + +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Managers.LanguageManager; +import backupmanager.Utils.AppPreferences; +import backupmanager.Utils.SystemForm; +import backupmanager.Utils.ToastUtils; +import backupmanager.gui.component.AccentColorIcon; +import backupmanager.gui.system.FormManager; +import backupmanager.gui.themes.PanelThemes; +import net.miginfocom.swing.MigLayout; +import raven.color.ColorPicker; +import raven.modal.Drawer; +import raven.modal.ModalDialog; +import raven.modal.component.SimpleModalBorder; +import raven.modal.drawer.DrawerBuilder; +import raven.modal.drawer.renderer.AbstractDrawerLineStyleRenderer; +import raven.modal.drawer.renderer.DrawerCurvedLineStyle; +import raven.modal.drawer.renderer.DrawerStraightDotLineStyle; +import raven.modal.drawer.simple.SimpleDrawerBuilder; +import raven.modal.option.LayoutOption; +import raven.modal.option.Location; +import raven.modal.option.Option; + +@SystemForm(name = "Setting", description = "application setting and configuration", tags = {"themes", "options"}) +public class FormSetting extends CustomForm { + + private static final Logger logger = LoggerFactory.getLogger(FormSetting.class); + private boolean languageInitializing = true; + + public FormSetting() { + build(); + } + + @Override + protected void init() { + setLayout(new MigLayout("fill", "[fill][fill,grow 0,250:250]", "[fill]")); + tabbedPane = new JTabbedPane(); + tabbedPane.putClientProperty(FlatClientProperties.STYLE, "" + + "tabType:card"); + + tabbedPane.addTab(Translations.get(TKey.SETTINGS_LAYOUT_TAB), createLayoutOption()); + tabbedPane.addTab(Translations.get(TKey.SETTINGS_STYLE_TAB), createStyleOption()); + add(tabbedPane, "gapy 1 0"); + add(createThemes()); + } + + @Override + protected void loadData() {} + + private JPanel createLayoutOption() { + JPanel panel = new JPanel(new MigLayout("wrap,fillx", "[fill]")); + panel.add(createWindowsLayout()); + panel.add(createDrawerLayout()); + panel.add(createModalDefaultOption()); + panel.add(createLanguageOption()); + return panel; + } + + private Component createWindowsLayout() { + JPanel panel = new JPanel(new MigLayout()); + windowsLayout = new TitledBorder("Windows Layout"); + panel.setBorder(windowsLayout); + chRightToLeft = new JCheckBox("Right to Left", !getComponentOrientation().isLeftToRight()); + chFullWindow = new JCheckBox("Full Window Content", FlatClientProperties.clientPropertyBoolean(FormManager.getFrame().getRootPane(), FlatClientProperties.FULL_WINDOW_CONTENT, false)); + chRightToLeft.addActionListener(e -> { + if (chRightToLeft.isSelected()) { + FormManager.getFrame().applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + } else { + FormManager.getFrame().applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + } + FormManager.getFrame().revalidate(); + }); + chFullWindow.addActionListener(e -> { + FormManager.getFrame().getRootPane().putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, chFullWindow.isSelected()); + }); + panel.add(chRightToLeft); + panel.add(chFullWindow); + return panel; + } + + private Component createDrawerLayout() { + JPanel panel = new JPanel(new MigLayout()); + drawerLayout = new TitledBorder("Drawer layout"); + panel.setBorder(drawerLayout); + + jrLeft = new JRadioButton("Left"); + jrLeading = new JRadioButton("Leading"); + jrTrailing = new JRadioButton("Trailing"); + jrRight = new JRadioButton("Right"); + jrTop = new JRadioButton("Top"); + jrBottom = new JRadioButton("Bottom"); + + ButtonGroup group = new ButtonGroup(); + group.add(jrLeft); + group.add(jrLeading); + group.add(jrTrailing); + group.add(jrRight); + group.add(jrTop); + group.add(jrBottom); + + jrLeading.setSelected(true); + + jrLeft.addActionListener(e -> { + DrawerBuilder drawerBuilder = Drawer.getDrawerBuilder(); + LayoutOption layoutOption = Drawer.getDrawerOption().getLayoutOption(); + layoutOption.setSize(drawerBuilder.getDrawerWidth(), 1f) + .setLocation(Location.LEFT, Location.TOP) + .setAnimateDistance(-0.7f, 0f); + getRootPane().revalidate(); + }); + jrLeading.addActionListener(e -> { + DrawerBuilder drawerBuilder = Drawer.getDrawerBuilder(); + LayoutOption layoutOption = Drawer.getDrawerOption().getLayoutOption(); + layoutOption.setSize(drawerBuilder.getDrawerWidth(), 1f) + .setLocation(Location.LEADING, Location.TOP) + .setAnimateDistance(-0.7f, 0f); + getRootPane().revalidate(); + }); + jrTrailing.addActionListener(e -> { + DrawerBuilder drawerBuilder = Drawer.getDrawerBuilder(); + LayoutOption layoutOption = Drawer.getDrawerOption().getLayoutOption(); + layoutOption.setSize(drawerBuilder.getDrawerWidth(), 1f) + .setLocation(Location.TRAILING, Location.TOP) + .setAnimateDistance(0.7f, 0f); + getRootPane().revalidate(); + }); + jrRight.addActionListener(e -> { + DrawerBuilder drawerBuilder = Drawer.getDrawerBuilder(); + LayoutOption layoutOption = Drawer.getDrawerOption().getLayoutOption(); + layoutOption.setSize(drawerBuilder.getDrawerWidth(), 1f) + .setLocation(Location.RIGHT, Location.TOP) + .setAnimateDistance(0.7f, 0f); + getRootPane().revalidate(); + }); + jrTop.addActionListener(e -> { + DrawerBuilder drawerBuilder = Drawer.getDrawerBuilder(); + LayoutOption layoutOption = Drawer.getDrawerOption().getLayoutOption(); + layoutOption.setSize(1f, drawerBuilder.getDrawerWidth()) + .setLocation(Location.LEADING, Location.TOP) + .setAnimateDistance(0f, -0.7f); + getRootPane().revalidate(); + }); + jrBottom.addActionListener(e -> { + DrawerBuilder drawerBuilder = Drawer.getDrawerBuilder(); + LayoutOption layoutOption = Drawer.getDrawerOption().getLayoutOption(); + layoutOption.setSize(1f, drawerBuilder.getDrawerWidth()) + .setLocation(Location.LEADING, Location.BOTTOM) + .setAnimateDistance(0f, 0.7f); + getRootPane().revalidate(); + }); + + panel.add(jrLeft); + panel.add(jrLeading); + panel.add(jrTrailing); + panel.add(jrRight); + panel.add(jrTop); + panel.add(jrBottom); + return panel; + } + + private Component createModalDefaultOption() { + JPanel panel = new JPanel(new MigLayout()); + modalOption = new TitledBorder("Default modal option"); + panel.setBorder(new TitledBorder(modalOption)); + chAnimation = new JCheckBox("Animation enable"); + chCloseOnPressedEscape = new JCheckBox("Close on pressed escape"); + chAnimation.setSelected(ModalDialog.getDefaultOption().isAnimationEnabled()); + chCloseOnPressedEscape.setSelected(ModalDialog.getDefaultOption().isCloseOnPressedEscape()); + + chAnimation.addActionListener(e -> ModalDialog.getDefaultOption().setAnimationEnabled(chAnimation.isSelected())); + chCloseOnPressedEscape.addActionListener(e -> ModalDialog.getDefaultOption().setCloseOnPressedEscape(chCloseOnPressedEscape.isSelected())); + + panel.add(chAnimation); + panel.add(chCloseOnPressedEscape); + + return panel; + } + + private Component createLanguageOption() { + JPanel panel = new JPanel(new MigLayout()); + languageTitleBorder = new TitledBorder("Language"); + panel.setBorder(languageTitleBorder); + languageCombo = new JComboBox<>(); + initComboItem(languageCombo); + + languageCombo.addActionListener(e -> { + if (languageInitializing) + return; + + String languageName = languageCombo.getSelectedItem().toString(); + + LanguageManager.setLanguage(languageName); + ToastUtils.showDefault(this, Translations.get(TKey.TOAST_LANGUAGE_CHANGE)); + }); + + languageCombo.setSelectedItem(LanguageManager.getLanguage().getLanguageName()); + panel.add(languageCombo); + languageInitializing = false; + + return panel; + } + + private void initComboItem(JComboBox<Object> combo) { + combo.addItem("English"); + combo.addItem("Italiano"); + combo.addItem("Español"); + combo.addItem("Deutsch"); + combo.addItem("Français"); + } + + private JPanel createStyleOption() { + JPanel panel = new JPanel(new MigLayout("wrap,fillx", "[fill]")); + panel.add(createAccentColor()); + panel.add(createDrawerStyle()); + return panel; + } + + private static final String[] accentColorKeys = { + "App.accent.default", "App.accent.blue", "App.accent.purple", "App.accent.red", + "App.accent.orange", "App.accent.yellow", "App.accent.green", + }; + private static final String[] accentColorNames = { + "Default", "Blue", "Purple", "Red", "Orange", "Yellow", "Green", + }; + private final JToggleButton[] accentColorButtons = new JToggleButton[accentColorKeys.length]; + private JToggleButton accentColorCustomButton; + private JToggleButton oldSelected; + + private Component createAccentColor() { + JPanel panel = new JPanel(new MigLayout()); + accentLayout = new TitledBorder("Accent color"); + panel.setBorder(accentLayout); + ButtonGroup group = new ButtonGroup(); + JToolBar toolBar = new JToolBar(); + toolBar.putClientProperty(FlatClientProperties.STYLE, "" + + "hoverButtonGroupBackground:null;"); + + boolean selected = false; + for (int i = 0; i < accentColorButtons.length; i++) { + accentColorButtons[i] = new JToggleButton(new AccentColorIcon(accentColorKeys[i])); + accentColorButtons[i].setToolTipText(accentColorNames[i]); + accentColorButtons[i].addActionListener(this::accentColorChanged); + toolBar.add(accentColorButtons[i]); + group.add(accentColorButtons[i]); + if (!selected) { + if (AppPreferences.accentColor == null) { + if (i == 0) { + accentColorButtons[i].setSelected(true); + oldSelected = accentColorButtons[i]; + selected = true; + } + } else { + Color color = UIManager.getColor(accentColorKeys[i]); + if (AppPreferences.accentColor.equals(color)) { + accentColorButtons[i].setSelected(true); + oldSelected = accentColorButtons[i]; + selected = true; + } + } + } + } + accentColorCustomButton = createCustomAccentColor(); + group.add(accentColorCustomButton); + toolBar.add(accentColorCustomButton); + if (!selected) { + accentColorCustomButton.setSelected(true); + } + + FlatLaf.setSystemColorGetter(name -> name.equals("accent") ? AppPreferences.accentColor : null); + UIManager.addPropertyChangeListener(e -> { + if ("lookAndFeel".equals(e.getPropertyName())) { + updateAccentColorButtons(); + } + }); + updateAccentColorButtons(); + panel.add(toolBar); + return panel; + } + + private JToggleButton createCustomAccentColor() { + JToggleButton button = new JToggleButton(new FlatSVGIcon("icons/color.svg", 16, 16)); + button.addActionListener(e -> { + ColorPicker colorPicker = new ColorPicker(AppPreferences.accentColor); + colorPicker.setBorder(new ScaledEmptyBorder(0, 20, 0, 20)); + Option option = ModalDialog.createOption(); + option.setAnimationEnabled(false); + colorPickerLayout = new SimpleModalBorder(colorPicker, Translations.get(TKey.SETTINGS_COLOR_PICKER_LAYOUT), SimpleModalBorder.YES_NO_OPTION, (controller, action) -> { + if (action == SimpleModalBorder.YES_OPTION) { + AppPreferences.accentColor = colorPicker.getSelectedColor(); + oldSelected = null; + applyAccentColor(); + } else if (action == SimpleModalBorder.CLOSE_OPTION) { + if (oldSelected != null) { + oldSelected.setSelected(true); + } + } + }); + ModalDialog.showModal(this, colorPickerLayout, option); + }); + return button; + } + + private Component createDrawerStyle() { + JPanel panel = new JPanel(new MigLayout("insets 0,filly", "[][][grow,fill]", "[fill]")); + JPanel lineStyle = new JPanel(new MigLayout("wrap", "[200]")); + JPanel lineStyleOption = new JPanel(new MigLayout("wrap", "[200]")); + JPanel lineColorOption = new JPanel(new MigLayout("wrap", "[200]")); + + drawerLineLayout = new TitledBorder("Drawer line style"); + lineStyleOptionLayout = new TitledBorder("Line style option"); + colorOptionLayout = new TitledBorder("Color option"); + + lineStyle.setBorder(drawerLineLayout); + lineStyleOption.setBorder(lineStyleOptionLayout); + lineColorOption.setBorder(colorOptionLayout); + + ButtonGroup groupStyle = new ButtonGroup(); + jrCurvedStyle = new JRadioButton("Curved line style"); + jrStraightDotStyle = new JRadioButton("Straight dot line style", true); + groupStyle.add(jrCurvedStyle); + groupStyle.add(jrStraightDotStyle); + + ButtonGroup groupStyleOption = new ButtonGroup(); + jrStyleOption1 = new JRadioButton("Rectangle"); + jrStyleOption2 = new JRadioButton("Ellipse", true); + groupStyleOption.add(jrStyleOption1); + groupStyleOption.add(jrStyleOption2); + + chPaintLineColor = new JCheckBox("Paint selected line color"); + + jrCurvedStyle.addActionListener(e -> { + if (jrCurvedStyle.isSelected()) { + jrStyleOption1.setText(Translations.get(TKey.SETTINGS_LINE_STYLE_LINE)); + jrStyleOption2.setText(Translations.get(TKey.SETTINGS_LINE_STYLE_CURVED)); + boolean round = jrStyleOption2.isSelected(); + boolean paintSelectedLine = chPaintLineColor.isSelected(); + setDrawerLineStyle(true, round, paintSelectedLine); + } + }); + jrStraightDotStyle.addActionListener(e -> { + if (jrStraightDotStyle.isSelected()) { + jrStyleOption1.setText(Translations.get(TKey.SETTINGS_LINE_STYLE_RETTANGLE)); + jrStyleOption2.setText(Translations.get(TKey.SETTINGS_LINE_STYLE_ELLIPSE)); + boolean round = jrStyleOption2.isSelected(); + boolean paintSelectedLine = chPaintLineColor.isSelected(); + setDrawerLineStyle(false, round, paintSelectedLine); + } + }); + + jrStyleOption1.addActionListener(e -> { + if (jrStyleOption1.isSelected()) { + boolean curved = jrCurvedStyle.isSelected(); + boolean paintSelectedLine = chPaintLineColor.isSelected(); + setDrawerLineStyle(curved, false, paintSelectedLine); + } + }); + + jrStyleOption2.addActionListener(e -> { + if (jrStyleOption2.isSelected()) { + boolean curved = jrCurvedStyle.isSelected(); + boolean paintSelectedLine = chPaintLineColor.isSelected(); + setDrawerLineStyle(curved, true, paintSelectedLine); + } + }); + + chPaintLineColor.addActionListener(e -> { + boolean curved = jrCurvedStyle.isSelected(); + boolean round = jrStyleOption2.isSelected(); + boolean paintSelectedLine = chPaintLineColor.isSelected(); + setDrawerLineStyle(curved, round, paintSelectedLine); + }); + + lineStyle.add(jrCurvedStyle); + lineStyle.add(jrStraightDotStyle); + + lineStyleOption.add(jrStyleOption1); + lineStyleOption.add(jrStyleOption2); + + lineColorOption.add(chPaintLineColor); + + panel.add(lineStyle); + panel.add(lineStyleOption); + panel.add(lineColorOption); + return panel; + } + + private void setDrawerLineStyle(boolean curved, boolean round, boolean color) { + AbstractDrawerLineStyleRenderer style; + if (curved) { + style = new DrawerCurvedLineStyle(round, color); + } else { + style = new DrawerStraightDotLineStyle(round, color); + } + ((SimpleDrawerBuilder) Drawer.getDrawerBuilder()).getSimpleMenuOption().getMenuStyle().setDrawerLineStyleRenderer(style); + ((SimpleDrawerBuilder) Drawer.getDrawerBuilder()).getDrawerMenu().repaint(); + } + + private void accentColorChanged(ActionEvent e) { + String accentColorKey = null; + for (int i = 0; i < accentColorButtons.length; i++) { + if (accentColorButtons[i].isSelected()) { + accentColorKey = accentColorKeys[i]; + oldSelected = accentColorButtons[i]; + break; + } + } + AppPreferences.accentColor = (accentColorKey != null && !accentColorKey.equals(accentColorKeys[0])) + ? UIManager.getColor(accentColorKey) + : null; + applyAccentColor(); + } + + private void applyAccentColor() { + Class<? extends LookAndFeel> lafClass = UIManager.getLookAndFeel().getClass(); + AppPreferences.updateAccentColor(AppPreferences.accentColor); + try { + FlatLaf.setup(lafClass.getDeclaredConstructor().newInstance()); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | NoSuchMethodException | SecurityException e) { + logger.warn("Error while trying to apply the accent color: {}", e); + } + FlatLaf.updateUI(); + } + + private void updateAccentColorButtons() { + Class<? extends LookAndFeel> lafClass = UIManager.getLookAndFeel().getClass(); + boolean isAccentColorSupported = + lafClass == FlatLightLaf.class || + lafClass == FlatDarkLaf.class || + lafClass == FlatIntelliJLaf.class || + lafClass == FlatDarculaLaf.class || + lafClass == FlatMacLightLaf.class || + lafClass == FlatMacDarkLaf.class; + for (JToggleButton btn : accentColorButtons) { + btn.setEnabled(isAccentColorSupported); + } + if (accentColorCustomButton != null) { + accentColorCustomButton.setEnabled(isAccentColorSupported); + } + } + + private JPanel createThemes() { + JPanel panel = new JPanel(new MigLayout("wrap,fill,insets 0", "[fill]", "[grow 0,fill]0[fill]")); + final PanelThemes panelThemes = new PanelThemes(); + JPanel panelHeader = new JPanel(new MigLayout("fillx,insets 3", "[grow 0]push[]")); + themeLabel = new JLabel("Themes"); + panelHeader.add(themeLabel); + JComboBox<Object> combo = new JComboBox<>(new Object[] {Translations.get(TKey.SETTINGS_THEME_ALL), Translations.get(TKey.SETTINGS_THEME_LIGHT), Translations.get(TKey.SETTINGS_THEME_DARK)} ); + combo.addActionListener(e -> { + panelThemes.updateThemesList(combo.getSelectedIndex()); + }); + panelHeader.add(combo); + panel.add(panelHeader); + panel.add(panelThemes); + return panel; + } + + @Override + public void setTranslations() { + windowsLayout.setTitle(Translations.get(TKey.SETTINGS_WINDOWS_LAYOUT)); + chRightToLeft.setText(Translations.get(TKey.SETTINGS_WINDOWS_RIGHT)); + chFullWindow.setText(Translations.get(TKey.SETTINGS_WINDOWS_FULL)); + drawerLayout.setTitle(Translations.get(TKey.SETTINGS_DRAWER_LAYOUT)); + jrLeft.setText(Translations.get(TKey.SETTINGS_DRAWER_LEFT)); + jrLeading.setText(Translations.get(TKey.SETTINGS_DRAWER_LEADING)); + jrTrailing.setText(Translations.get(TKey.SETTINGS_DRAWER_TRAILING)); + jrRight.setText(Translations.get(TKey.SETTINGS_DRAWER_RIGHT)); + jrTop.setText(Translations.get(TKey.SETTINGS_DRAWER_TOP)); + jrBottom.setText(Translations.get(TKey.SETTINGS_DRAWER_BOTTOM)); + modalOption.setTitle(Translations.get(TKey.SETTINGS_MODAL_OPTION)); + chAnimation.setText(Translations.get(TKey.SETTINGS_MODAL_ANIMATION)); + chCloseOnPressedEscape.setText(Translations.get(TKey.SETTINGS_MODAL_CLOSE)); + languageTitleBorder.setTitle(Translations.get(TKey.SETTINGS_LANGUAGES_LAYOUT)); + accentLayout.setTitle(Translations.get(TKey.SETTINGS_ACCENT_LAYOUT)); + // colorPickerLayout.setTitle(Translations.get(TKey.SETTINGS_COLOR_PICKER_LAYOUT)); // the method is not actually avaiable + drawerLineLayout.setTitle(Translations.get(TKey.SETTINGS_DRAWER_LINE_LAYOUT)); + lineStyleOptionLayout.setTitle(Translations.get(TKey.SETTINGS_LINE_STYLE_LAYOUT)); + colorOptionLayout.setTitle(Translations.get(TKey.SETTINGS_COLOR_OPTION_LAYOUT)); + jrCurvedStyle.setText(Translations.get(TKey.SETTINGS_DRAWER_LINE_CURVED)); + jrStraightDotStyle.setText(Translations.get(TKey.SETTINGS_DRAWER_DOT_LINE)); + jrStyleOption1.setText(Translations.get(TKey.SETTINGS_LINE_STYLE_RETTANGLE)); + jrStyleOption2.setText(Translations.get(TKey.SETTINGS_LINE_STYLE_ELLIPSE)); + chPaintLineColor.setText(Translations.get(TKey.SETTINGS_COLOR_OPTION_PAINTED)); + themeLabel.setText(Translations.get(TKey.SETTINGS_THEMES)); + } + + private JTabbedPane tabbedPane; + private JComboBox<Object> languageCombo; + private TitledBorder windowsLayout; + private JCheckBox chRightToLeft; + private JCheckBox chFullWindow; + private TitledBorder drawerLayout; + private JRadioButton jrLeft; + private JRadioButton jrLeading; + private JRadioButton jrTrailing; + private JRadioButton jrRight; + private JRadioButton jrTop; + private JRadioButton jrBottom; + private TitledBorder modalOption; + private JCheckBox chAnimation; + private JCheckBox chCloseOnPressedEscape; + private TitledBorder languageTitleBorder; + private TitledBorder accentLayout; + private SimpleModalBorder colorPickerLayout; + private TitledBorder drawerLineLayout; + private TitledBorder lineStyleOptionLayout; + private TitledBorder colorOptionLayout; + private JRadioButton jrCurvedStyle; + private JRadioButton jrStraightDotStyle; + private JRadioButton jrStyleOption1; + private JRadioButton jrStyleOption2; + private JCheckBox chPaintLineColor; + private JLabel themeLabel; +} diff --git a/src/main/java/backupmanager/gui/frames/BackupManager.java b/src/main/java/backupmanager/gui/frames/BackupManager.java new file mode 100644 index 00000000..affd8890 --- /dev/null +++ b/src/main/java/backupmanager/gui/frames/BackupManager.java @@ -0,0 +1,58 @@ +package backupmanager.gui.frames; + +import java.awt.Dimension; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JFrame; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.Enums.ConfigKey; +import backupmanager.gui.Controllers.GuiController; +import backupmanager.gui.menu.DrawerManager; +import backupmanager.gui.system.FormManager; + +public class BackupManager extends JFrame { + + private static BackupManager instance; + + private BackupManager() { + init(); + } + + public static synchronized BackupManager getInstance() { + if (instance == null) { + instance = new BackupManager(); + } + return instance; + } + + private void init() { + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + setVisible(false); + } + }); + + setTitle("Backup Manager"); + this.setIconImage(GuiController.getIcon(this.getClass())); + getRootPane().putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, true); + DrawerManager.getInstance().install(this); + FormManager.install(this); + + setSize(new Dimension( + Integer.parseInt(ConfigKey.GUI_WIDTH.getValue()), + Integer.parseInt(ConfigKey.GUI_HEIGHT.getValue()) + )); + setMinimumSize(new Dimension( + Integer.parseInt(ConfigKey.GUI_MIN_WIDTH.getValue()), + Integer.parseInt(ConfigKey.GUI_MIN_HEIGHT.getValue()) + )); + + setLocationRelativeTo(null); + } +} diff --git a/src/main/java/backupmanager/GUI/BackupProgressGUI.form b/src/main/java/backupmanager/gui/frames/BackupProgressGUI.form similarity index 100% rename from src/main/java/backupmanager/GUI/BackupProgressGUI.form rename to src/main/java/backupmanager/gui/frames/BackupProgressGUI.form diff --git a/src/main/java/backupmanager/GUI/BackupProgressGUI.java b/src/main/java/backupmanager/gui/frames/BackupProgressGUI.java similarity index 89% rename from src/main/java/backupmanager/GUI/BackupProgressGUI.java rename to src/main/java/backupmanager/gui/frames/BackupProgressGUI.java index 06840476..ec18a2ed 100644 --- a/src/main/java/backupmanager/GUI/BackupProgressGUI.java +++ b/src/main/java/backupmanager/gui/frames/BackupProgressGUI.java @@ -1,9 +1,9 @@ -package backupmanager.GUI; +package backupmanager.gui.frames; -import backupmanager.Controllers.GuiController; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; -import backupmanager.GUI.Controllers.BackupProgressController; +import backupmanager.gui.Controllers.GuiController; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.gui.frames.Controllers.BackupProgressController; public class BackupProgressGUI extends javax.swing.JDialog { @@ -34,10 +34,10 @@ public void updateProgressBar(int value, String fileProcessed, int filesCopiedSo fileZippedLabel.setText(fileProcessed); // edit the title with counts - setTitle(TranslationCategory.PROGRESS_BACKUP_FRAME.getTranslation(TranslationKey.PROGRESS_BACKUP_TITLE) + " - " + filesCopiedSoFar + "/" + totalFilesCount); + setTitle(Translations.get(TKey.PROGRESS_BACKUP_TITLE) + " - " + filesCopiedSoFar + "/" + totalFilesCount); if (value == 100) { - loadingMessageLabel.setText(TranslationCategory.PROGRESS_BACKUP_FRAME.getTranslation(TranslationKey.STATUS_COMPLETED)); + loadingMessageLabel.setText(Translations.get(TKey.STATUS_COMPLETED)); closeButton.setEnabled(true); CancelButton.setEnabled(false); fileZippedLabel.setText(""); @@ -155,10 +155,10 @@ private void CancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN- }//GEN-LAST:event_CancelButtonActionPerformed private void setTranslations() { - setTitle(TranslationCategory.PROGRESS_BACKUP_FRAME.getTranslation(TranslationKey.PROGRESS_BACKUP_TITLE)); - CancelButton.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.CANCEL_BUTTON)); - closeButton.setText(TranslationCategory.GENERAL.getTranslation(TranslationKey.CLOSE_BUTTON)); - loadingMessageLabel.setText(TranslationCategory.PROGRESS_BACKUP_FRAME.getTranslation(TranslationKey.STATUS_LOADING)); + setTitle(Translations.get(TKey.PROGRESS_BACKUP_TITLE)); + CancelButton.setText(Translations.get(TKey.CANCEL_BUTTON)); + closeButton.setText(Translations.get(TKey.CLOSE_BUTTON)); + loadingMessageLabel.setText(Translations.get(TKey.STATUS_LOADING)); } // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/src/main/java/backupmanager/gui/frames/Controllers/BackupManagerController.java b/src/main/java/backupmanager/gui/frames/Controllers/BackupManagerController.java new file mode 100644 index 00000000..bfeb31f2 --- /dev/null +++ b/src/main/java/backupmanager/gui/frames/Controllers/BackupManagerController.java @@ -0,0 +1,124 @@ +package backupmanager.gui.frames.Controllers; + +import java.util.ArrayList; +import java.util.List; + +import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Services.BackupService; +import backupmanager.Utils.ToastUtils; +import backupmanager.gui.Table.BackupTableDataService; +import backupmanager.gui.forms.CustomForm; +import backupmanager.gui.simple.BackupEntryDialog; +import raven.modal.ModalDialog; +import raven.modal.component.SimpleModalBorder; +import raven.modal.option.Location; +import raven.modal.option.Option; + +public class BackupManagerController { + + private final BackupService backupService; + private final BackupTableDataService backupTable; + + public BackupManagerController(BackupService backupService, BackupTableDataService backupTable) { + this.backupService = backupService; + this.backupTable = backupTable; + } + + public List<ConfigurationBackup> researchInTableAndGet(List<ConfigurationBackup> backups, String research) { + List<ConfigurationBackup> tempBackups = new ArrayList<>(); + research = research.toLowerCase(); + + for (ConfigurationBackup backup : backups) { + if (backup.getName().toLowerCase().contains(research) || + backup.getTargetPath().toLowerCase().contains(research) || + backup.getDestinationPath().toLowerCase().contains(research) || + (backup.getLastBackupDate() != null && backup.getLastBackupDate().toString().toLowerCase().contains(research)) || + (backup.getNextBackupDate() != null && backup.getNextBackupDate().toString().toLowerCase().contains(research)) || + (backup.getTimeIntervalBackup() != null && backup.getTimeIntervalBackup().toString().toLowerCase().contains(research))) { + tempBackups.add(backup); + } + } + + return tempBackups; + } + + public void showCreateModal(CustomForm form) { + Option option = ModalDialog.createOption(); + option.getLayoutOption() + .setSize(-1, 1f) + .setLocation(Location.TRAILING, Location.TOP) + .setAnimateDistance(0.7f, 0); + + BackupEntryDialog dialog = new BackupEntryDialog(backupTable); + + ModalDialog.showModal( + form, + new SimpleModalBorder( + dialog, + Translations.get(TKey.PAGE_SUBTITLE_CREATE), + SimpleModalBorder.OK_CANCEL_OPTION, + (controller, action) -> { + if (action == SimpleModalBorder.OK_OPTION) { + if (!dialog.canDispose()) { + return; + } + + ConfigurationBackup backup = dialog.getResult(); + backupService.updateBackup(backup); + + ToastUtils.showSuccess(form, Translations.get(TKey.TOAST_BACKUP_CREATED)); + + form.formRefresh(); + controller.close(); + } + } + ), + option + ); + } + + public void showEditModal(CustomForm form, ConfigurationBackup backup) { + BackupEntryDialog dialog = new BackupEntryDialog(backupTable, backup); + + Option option = ModalDialog.createOption(); + option.getLayoutOption() + .setSize(-1, 1f) + .setLocation(Location.TRAILING, Location.TOP) + .setAnimateDistance(0.7f, 0); + + ModalDialog.showModal( + form, + new SimpleModalBorder( + dialog, + Translations.get(TKey.PAGE_SUBTITLE_EDIT), + SimpleModalBorder.OK_CANCEL_OPTION, + (controller, action) -> { + if (action == SimpleModalBorder.OK_OPTION) { + if (!dialog.canDispose()) { + return; + } + + ConfigurationBackup editedBackup = dialog.getResult(); + backupService.updateBackup(editedBackup); + + ToastUtils.showSuccess(form, Translations.get(TKey.TOAST_BACKUP_EDITED)); + + form.formRefresh(); + controller.close(); + } + } + ), + option + ); + } + + public List<ConfigurationBackup> getAllBackups() { + return backupService.getAllBackups(); + } + + public String buildDetails(ConfigurationBackup backup) { + return backupService.buildDetails(backup); + } +} diff --git a/src/main/java/backupmanager/gui/frames/Controllers/BackupPopupController.java b/src/main/java/backupmanager/gui/frames/Controllers/BackupPopupController.java new file mode 100644 index 00000000..373d3dc6 --- /dev/null +++ b/src/main/java/backupmanager/gui/frames/Controllers/BackupPopupController.java @@ -0,0 +1,199 @@ +package backupmanager.gui.frames.Controllers; + +import java.awt.Desktop; +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import backupmanager.BackupOperations; +import backupmanager.Entities.BackupExecutionContext; +import backupmanager.Entities.BackupUIContext; +import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Entities.ZippingContext; +import backupmanager.Enums.BackupTriggerType; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Helpers.BackupHelper; +import backupmanager.Managers.ExceptionManager; +import backupmanager.Utils.ToastUtils; +import backupmanager.database.Repositories.BackupConfigurationRepository; +import backupmanager.gui.Table.BackupTableDataService; +import backupmanager.gui.frames.BackupProgressGUI; + +public class BackupPopupController { + + private static final Logger logger = LoggerFactory.getLogger(BackupPopupController.class); + + public static void popupItemInterrupt() { + // TODO: add it + } + + public static void popupItemRenameBackup(JComponent parent, List<ConfigurationBackup> backups, ConfigurationBackup backup) { + renameBackup(parent, backups, backup); + } + + public static void popupItemOpenDestinationPath(JComponent parent, ConfigurationBackup backup) { + openFolder(parent, backup.getDestinationPath()); + } + + public static void popupItemOpenInitialPath(JComponent parent, ConfigurationBackup backup) { + openFolder(parent, backup.getTargetPath()); + } + + public static void popupItemAutoBackup(JComponent parent, ConfigurationBackup backup) { + BackupHelper.toggleAutomaticBackup(parent, backup); + } + + public static void popupItemRunBackup(ConfigurationBackup backup, BackupTableDataService backupTable, JMenuItem interruptBackupPopupItem, JMenuItem RunBackupPopupItem) { + BackupProgressGUI progressBar = new BackupProgressGUI(backup.getTargetPath(), backup.getDestinationPath()); + + ZippingContext context = new ZippingContext( + BackupExecutionContext.create(backup), + new BackupUIContext(null, backupTable, progressBar, interruptBackupPopupItem, RunBackupPopupItem) + ); + + BackupOperations.requestSingleBackup(context, BackupTriggerType.USER); + } + + public static void popupItemCopyDestinationPath(ConfigurationBackup backup) { + StringSelection selection = new StringSelection(backup.getDestinationPath()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); + } + + public static void popupItemCopyInitialPath(ConfigurationBackup backup) { + StringSelection selection = new StringSelection(backup.getTargetPath()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); + } + + public static void popupItemCopyBackupName(ConfigurationBackup backup) { + StringSelection selection = new StringSelection(backup.getName()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); + } + + public static void popupItemDuplicateBackup(ConfigurationBackup backup) { + logger.info("Event --> duplicating backup"); + + int value = getIncrementalBackupNameValue(backup.getName()); + + LocalDateTime dateNow = LocalDateTime.now(); + ConfigurationBackup newBackup = new ConfigurationBackup( + backup.getName() + "(" + value + ")", + backup.getTargetPath(), + backup.getDestinationPath(), + null, + backup.isAutomatic(), + backup.getNextBackupDate(), + backup.getTimeIntervalBackup(), + backup.getNotes(), + dateNow, + dateNow, + 0, + backup.getMaxToKeep() + ); + + BackupConfigurationRepository.insertBackup(newBackup); + } + + private static int getIncrementalBackupNameValue(String backupName) { + var backups = BackupConfigurationRepository.getBackupList(); + int max = 0; + + Pattern pattern = Pattern.compile( + Pattern.quote(backupName) + "\\((\\d+)\\)$" + ); + + for (var backup : backups) { + Matcher matcher = pattern.matcher(backup.getName()); + if (matcher.find()) { + int value = Integer.parseInt(matcher.group(1)); + max = Math.max(max, value); + } + } + + return max + 1; + } + + private static void renameBackup(JComponent parent, List<ConfigurationBackup> backups, ConfigurationBackup backup) { + logger.info("Event --> backup renaming"); + + String backupName = getBackupNameFromInputDialog(parent, backups, backup.getName(), false); + if (backupName == null || backupName.isEmpty()) return; + + backup.setName(backupName); + backup.setLastUpdateDate(LocalDateTime.now()); + BackupHelper.updateBackup(backup); + } + + private static String getBackupNameFromInputDialog(JComponent parent, List<ConfigurationBackup> backups, String oldName, boolean canOverwrite) { + while (true) { + String backupName = JOptionPane.showInputDialog(null, Translations.get(TKey.BACKUP_NAME_INPUT), oldName); + + // If the user cancels the operation + if (backupName == null || backupName.trim().isEmpty()) { + return null; + } + + Optional<ConfigurationBackup> existingBackup = backups.stream() + .filter(b -> b.getName().equals(backupName)) + .findFirst(); + + if (existingBackup.isPresent()) { + if (canOverwrite) { + int response = JOptionPane.showConfirmDialog(null, Translations.get(TKey.DUPLICATED_BACKUP_NAME_MESSAGE), Translations.get(TKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + + if (response == JOptionPane.YES_OPTION) { + backups.remove(existingBackup.get()); + return backupName; + } + } else { + logger.warn("Backup name '{}' is already in use", backupName); + ToastUtils.showError(parent, Translations.get(TKey.TOAST_BACKUP_NAME_ALREADY_USED)); + } + } else { + return backupName; // Return valid name + } + } + } + + private static void openFolder(JComponent parent, String path) { + logger.info("Event --> opening folder"); + + File folder = new File(path); + + // if the object is a file i want to obtain the folder that contains that file + if (folder.exists() && folder.isFile()) { + folder = folder.getParentFile(); + } + + if (folder.exists() && folder.isDirectory()) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.open(folder); + } catch (IOException ex) { + logger.error("An error occurred: " + ex.getMessage(), ex); + ExceptionManager.openExceptionMessage(ex.getMessage(), Arrays.toString(ex.getStackTrace())); + } + } else { + logger.warn("Desktop not supported on this operating system"); + } + } else { + logger.warn("The folder does not exist or is invalid"); + ToastUtils.showError(parent, Translations.get(TKey.TOAST_FOLDER_NOT_EXISTING)); + } + } +} diff --git a/src/main/java/backupmanager/GUI/Controllers/BackupProgressController.java b/src/main/java/backupmanager/gui/frames/Controllers/BackupProgressController.java similarity index 53% rename from src/main/java/backupmanager/GUI/Controllers/BackupProgressController.java rename to src/main/java/backupmanager/gui/frames/Controllers/BackupProgressController.java index 70562398..b1bd5d96 100644 --- a/src/main/java/backupmanager/GUI/Controllers/BackupProgressController.java +++ b/src/main/java/backupmanager/gui/frames/Controllers/BackupProgressController.java @@ -1,15 +1,15 @@ -package backupmanager.GUI.Controllers; +package backupmanager.gui.frames.Controllers; import javax.swing.JOptionPane; -import backupmanager.Enums.TranslationLoaderEnum.TranslationCategory; -import backupmanager.Enums.TranslationLoaderEnum.TranslationKey; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; import backupmanager.Services.ZippingThread; public class BackupProgressController { public void handleCancelButtonRequest(javax.swing.JDialog dialog) { - int response = JOptionPane.showConfirmDialog(dialog, TranslationCategory.DIALOGS.getTranslation(TranslationKey.INTERRUPT_BACKUP_PROCESS_MESSAGE), TranslationCategory.DIALOGS.getTranslation(TranslationKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + int response = JOptionPane.showConfirmDialog(dialog, Translations.get(TKey.INTERRUPT_BACKUP_PROCESS_MESSAGE), Translations.get(TKey.CONFIRMATION_REQUIRED_TITLE), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (response == JOptionPane.YES_OPTION) { cancelBackup(); dialog.dispose(); diff --git a/src/main/java/backupmanager/gui/menu/DrawerManager.java b/src/main/java/backupmanager/gui/menu/DrawerManager.java new file mode 100644 index 00000000..631844a1 --- /dev/null +++ b/src/main/java/backupmanager/gui/menu/DrawerManager.java @@ -0,0 +1,64 @@ +package backupmanager.gui.menu; + +import javax.swing.JFrame; + +import backupmanager.Managers.LanguageManager; +import backupmanager.interfaces.ITranslatable; +import raven.modal.Drawer; +import raven.modal.drawer.DrawerPanel; +import raven.modal.drawer.item.MenuItem; + +public class DrawerManager implements ITranslatable { + + private DrawerPanel drawerPanel; + private MenuItem[] menuItems; + private JFrame parent; + + private static DrawerManager instance; + + public static DrawerManager getInstance() { + if (instance == null) { + instance = new DrawerManager(); + } + return instance; + } + + public void install(JFrame frame) { + parent = frame; + MyDrawerBuilder builder = new MyDrawerBuilder(); + + drawerPanel = builder.createDrawer(); + menuItems = builder.getSimpleMenuOption().getMenus(); + + Drawer.installDrawer(parent, builder); + } + + private DrawerManager() { + LanguageManager.register(this); + rebuildDrawer(); + } + + private void rebuildDrawer() { + MyDrawerBuilder builder = new MyDrawerBuilder(); + drawerPanel = builder.createDrawer(); + } + + public DrawerPanel getDrawer() { + return drawerPanel; + } + + public MenuItem[] getMenuItems() { + return menuItems; + } + + public JFrame getParent() { + return parent; + } + + // it's not the best rebuild the menu every time the language is changed but right now there is no a method to update + // the MenuItem title textin the raven library + @Override + public void setTranslations() { + rebuildDrawer(); + } +} diff --git a/src/main/java/backupmanager/gui/menu/MyDrawerBuilder.java b/src/main/java/backupmanager/gui/menu/MyDrawerBuilder.java new file mode 100644 index 00000000..11c8e6f4 --- /dev/null +++ b/src/main/java/backupmanager/gui/menu/MyDrawerBuilder.java @@ -0,0 +1,328 @@ +package backupmanager.gui.menu; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.UIManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Entities.Configurations; +import backupmanager.Enums.ConfigKey; +import backupmanager.Enums.MenuItems; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Json.JsonConfig; +import backupmanager.Managers.ExportManager; +import backupmanager.Managers.WebsiteManager; +import backupmanager.database.Repositories.BackupConfigurationRepository; +import backupmanager.gui.forms.FormBackupDashboard; +import backupmanager.gui.forms.FormBackupTable; +import backupmanager.gui.forms.FormHistory; +import backupmanager.gui.forms.FormSetting; +import backupmanager.gui.system.AllForms; +import backupmanager.gui.system.FormManager; +import raven.extras.AvatarIcon; +import raven.modal.drawer.DrawerPanel; +import raven.modal.drawer.item.Item; +import raven.modal.drawer.item.MenuItem; +import raven.modal.drawer.menu.MenuOption; +import raven.modal.drawer.menu.MenuStyle; +import raven.modal.drawer.renderer.DrawerStraightDotLineStyle; +import raven.modal.drawer.simple.SimpleDrawerBuilder; +import raven.modal.drawer.simple.footer.LightDarkButtonFooter; +import raven.modal.drawer.simple.footer.SimpleFooterData; +import raven.modal.drawer.simple.header.SimpleHeader; +import raven.modal.drawer.simple.header.SimpleHeaderData; +import raven.modal.option.Option; +import raven.modal.utils.FlatLafStyleUtils; + +public class MyDrawerBuilder extends SimpleDrawerBuilder { + private static final Logger logger = LoggerFactory.getLogger(MyDrawerBuilder.class); + + private static final Map<MenuItems, Runnable> menuActionMap = new HashMap<>(); + private static final Map<String, MenuItems> menuBindingMap = new HashMap<>(); + + public MyDrawerBuilder() { + super(createSimpleMenuOption()); + initMenuActions(); + LightDarkButtonFooter lightDarkButtonFooter = (LightDarkButtonFooter) getFooter(); + lightDarkButtonFooter.addModeChangeListener(isDarkMode -> { + // event for light dark mode changed + }); + } + + public void initHeader() { + // setup drawer header + SimpleHeader header = (SimpleHeader) getHeader(); + SimpleHeaderData data = header.getSimpleHeaderData(); + AvatarIcon icon = (AvatarIcon) data.getIcon(); + + icon.setIcon(new FlatSVGIcon("drawer/logo.svg", 100, 100)); + data.setTitle("Backup Manager"); + data.setDescription(ConfigKey.EMAIL.getValue()); + header.setSimpleHeaderData(data); + + rebuildMenu(); + } + + @Override + public SimpleHeaderData getSimpleHeaderData() { + AvatarIcon icon = new AvatarIcon(new FlatSVGIcon("drawer/logo.svg", 100, 100), 50, 50, 3.5f); + icon.setType(AvatarIcon.Type.MASK_SQUIRCLE); + icon.setBorder(2, 2); + + changeAvatarIconBorderColor(icon); + + UIManager.addPropertyChangeListener(evt -> { + if (evt.getPropertyName().equals("lookAndFeel")) { + changeAvatarIconBorderColor(icon); + } + }); + + return new SimpleHeaderData() + .setIcon(icon) + .setTitle("Backup Manager") + .setDescription(ConfigKey.EMAIL.getValue()); + } + + private static void initMenuActions() { + menuActionMap.put(MenuItems.BackupList, () -> + FormManager.showForm(AllForms.getForm(FormBackupTable.class))); + + menuActionMap.put(MenuItems.Dashboard, () -> + FormManager.showForm(AllForms.getForm(FormBackupDashboard.class))); + + menuActionMap.put(MenuItems.Export, () -> + ExportManager.exportAsCSV(DrawerManager.getInstance().getParent(), BackupConfigurationRepository.getBackupList(), ConfigurationBackup.getCSVHeader())); + + menuActionMap.put(MenuItems.Settings, () -> + FormManager.showForm(AllForms.getForm(FormSetting.class))); + + menuActionMap.put(MenuItems.History, () -> + FormManager.showForm(AllForms.getForm(FormHistory.class))); + + menuActionMap.put(MenuItems.InfoPage, () -> + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), ConfigKey.INFO_PAGE_LINK.getValue())); + + menuActionMap.put(MenuItems.BugReport, () -> + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), ConfigKey.ISSUE_PAGE_LINK.getValue())); + + menuActionMap.put(MenuItems.ContactUs, () -> + WebsiteManager.sendEmail(DrawerManager.getInstance().getParent())); + + menuActionMap.put(MenuItems.PaypalDonate, () -> + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), ConfigKey.DONATE_PAYPAL_LINK.getValue())); + + menuActionMap.put(MenuItems.BuymeacoffeeDonate, () -> + WebsiteManager.openWebSite(DrawerManager.getInstance().getParent(), ConfigKey.DONATE_BUYMEACOFFE_LINK.getValue())); + + menuActionMap.put(MenuItems.Subscription, () -> + FormManager.showSubscription()); + + menuActionMap.put(MenuItems.About, () -> + FormManager.showAbout()); + } + + private void changeAvatarIconBorderColor(AvatarIcon icon) { + icon.setBorderColor(new AvatarIcon.BorderColor(UIManager.getColor("Component.accentColor"), 0.7f)); + } + + @Override + public SimpleFooterData getSimpleFooterData() { + return new SimpleFooterData() + .setTitle("Swing Modal Dialog") + .setDescription("Version " + ConfigKey.VERSION.getValue()); + } + + @Override + public Option createOption() { + Option option = super.createOption(); + option.setOpacity(0.3f); + return option; + } + + public static MenuOption createSimpleMenuOption() { + MenuOption simpleMenuOption = new MenuOption(); + MenuItem[] items = buildMenuItems().toArray(MenuItem[]::new); + + simpleMenuOption.setMenuStyle(new MenuStyle() { + + @Override + public void styleMenuItem(JButton menu, int[] index, boolean isMainItem) { + boolean isTopLevel = index.length == 1; + + if (isTopLevel) { + + if (menu.getIcon() instanceof FlatSVGIcon svgIcon) { + FlatSVGIcon newIcon = new FlatSVGIcon(svgIcon.getName(), 20, 20); + menu.setIcon(newIcon); + } + + menu.putClientProperty(FlatClientProperties.STYLE, "margin:-1,0,-1,0;"); + } + } + + @Override + public void styleMenu(JComponent component) { + component.putClientProperty(FlatClientProperties.STYLE, + getDrawerBackgroundStyle()); + } + }); + + simpleMenuOption.getMenuStyle().setDrawerLineStyleRenderer(new DrawerStraightDotLineStyle()); + simpleMenuOption.setMenuValidation(new MyMenuValidation()); + + simpleMenuOption.addMenuEvent((action, index) -> { + logger.debug("Drawer menu selected " + Arrays.toString(index)); + MenuItems menuItem = resolveMenuItem(action); + + if (menuItem != null && menuActionMap.containsKey(menuItem)) { + menuActionMap.get(menuItem).run(); + action.consume(); + } + }); + + simpleMenuOption.setMenus(items).setBaseIconPath("drawer/icon/"); + + return simpleMenuOption; + } + + private static Item createMenuItem(String label, String icon, MenuItems menuEnum, Class<?> formClass) { + Item item = new Item(label, icon, formClass); + menuBindingMap.put(label.replace(" ", "").toLowerCase(), menuEnum); + return item; + } + + private static MenuItems resolveMenuItem(raven.modal.drawer.menu.MenuAction action) { + String name = action.getItem().getName(); + + if (name == null) + return null; + + String key = name.replace(" ", "").toLowerCase(); + return menuBindingMap.get(key); + } + + @Override + public int getOpenDrawerAt() { + return 1000; + } + + @Override + public boolean openDrawerAtScale() { + return false; + } + + @Override + public void build(DrawerPanel drawerPanel) { + drawerPanel.putClientProperty(FlatClientProperties.STYLE, getDrawerBackgroundStyle()); + FlatLafStyleUtils.appendStyle(drawerPanel, "border:0,0,0,1,$Separator.foreground;"); + } + + private static String getDrawerBackgroundStyle() { + return "background:$Menu.background;"; + } + + public DrawerPanel createDrawer() { + Option option = createOption(); + return new DrawerPanel(this, option); + } + + private static List<MenuItem> buildMenuItems() { + JsonConfig config = JsonConfig.getInstance(); + List<MenuItem> itemList = new ArrayList<>(); + + itemList.add(new Item.Label(Translations.get(TKey.SUBMENU_MAIN))); + + // Backup menu + Item backupItem = createMenuItem(Translations.get(TKey.BACKUP_TABLE), "forms.svg", MenuItems.BackupList, FormBackupTable.class); + + if (config.isMenuItemEnabled(MenuItems.Import.name())) { + backupItem.subMenu(Translations.get(TKey.IMPORT_BACKUP)); + bindSubMenu(Translations.get(TKey.IMPORT_BACKUP), MenuItems.Import); + } + + if (config.isMenuItemEnabled(MenuItems.Export.name())) { + backupItem.subMenu(Translations.get(TKey.EXPORT_BACKUP)); + bindSubMenu(Translations.get(TKey.EXPORT_BACKUP), MenuItems.Export); + } + + itemList.add(backupItem); + + // Dashboard + itemList.add(createMenuItem(Translations.get(TKey.DASHBOARD), "dashboard.svg", MenuItems.Dashboard, FormBackupDashboard.class)); + + itemList.add(new Item.Label(Translations.get(TKey.SUBMENU_OTHER))); + + // Settings + itemList.add(createMenuItem(Translations.get(TKey.OPTIONS), "setting.svg", MenuItems.Settings, FormSetting.class)); + + // History (configurable) + if (config.isMenuItemEnabled(MenuItems.History.name())) { + itemList.add(createMenuItem(Translations.get(TKey.HISTORY), "history.svg", MenuItems.History, FormHistory.class)); + } + + // External link (no form class) + itemList.add(createMenuItem(Translations.get(TKey.GITHUB_PAGE), "github.svg", MenuItems.InfoPage, null)); + + // Donate section + if (config.isMenuItemEnabled(MenuItems.Donate.name())) { + + Item donateItem = createMenuItem(Translations.get(TKey.DONATE), "donate.svg", MenuItems.Donate, null); + + if (config.isMenuItemEnabled(MenuItems.PaypalDonate.name())) { + donateItem.subMenu(Translations.get(TKey.PAYPAL)); + bindSubMenu(Translations.get(TKey.PAYPAL), MenuItems.PaypalDonate); + } + + if (config.isMenuItemEnabled(MenuItems.BuymeacoffeeDonate.name())) { + donateItem.subMenu(Translations.get(TKey.BUYMEACOFFE)); + bindSubMenu(Translations.get(TKey.BUYMEACOFFE), MenuItems.BuymeacoffeeDonate); + } + + itemList.add(donateItem); + } + + // Support section + if (config.isMenuItemEnabled(MenuItems.Support.name())) { + + Item helpItem = createMenuItem(Translations.get(TKey.HELP), "help.svg", MenuItems.Support, null); + + if (config.isMenuItemEnabled(MenuItems.BugReport.name())) { + helpItem.subMenu(Translations.get(TKey.BUG_REPORT)); + bindSubMenu(Translations.get(TKey.BUG_REPORT), MenuItems.BugReport); + } + + if (config.isMenuItemEnabled(MenuItems.ContactUs.name())) { + helpItem.subMenu(Translations.get(TKey.CONTACT_US)); + bindSubMenu(Translations.get(TKey.CONTACT_US), MenuItems.ContactUs); + } + + itemList.add(helpItem); + } + + if (Configurations.isSubscriptionNedded()) { + itemList.add(createMenuItem(Translations.get(TKey.SUBSCRIPTION), "subscription.svg", MenuItems.Subscription, null)); + } + + // About (LAST ITEM) + itemList.add(createMenuItem(Translations.get(TKey.ABOUT), "about.svg", MenuItems.About, null)); + + return itemList; + } + + private static void bindSubMenu(String label, MenuItems menuEnum) { + menuBindingMap.put(label.replace(" ", "").toLowerCase(), menuEnum); + } +} diff --git a/src/main/java/backupmanager/gui/menu/MyMenuValidation.java b/src/main/java/backupmanager/gui/menu/MyMenuValidation.java new file mode 100644 index 00000000..e257a31c --- /dev/null +++ b/src/main/java/backupmanager/gui/menu/MyMenuValidation.java @@ -0,0 +1,12 @@ +package backupmanager.gui.menu; + +import raven.modal.Drawer; +import backupmanager.gui.system.Form; +import raven.modal.drawer.menu.MenuValidation; + +public class MyMenuValidation extends MenuValidation { + public static boolean validation(Class<? extends Form> itemClass) { + int[] index = Drawer.getMenuIndexClass(itemClass); + return index != null; + } +} diff --git a/src/main/java/backupmanager/gui/simple/BackupEntryDialog.java b/src/main/java/backupmanager/gui/simple/BackupEntryDialog.java new file mode 100644 index 00000000..17ec2bb1 --- /dev/null +++ b/src/main/java/backupmanager/gui/simple/BackupEntryDialog.java @@ -0,0 +1,353 @@ +package backupmanager.gui.simple; + +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.time.LocalDateTime; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.SpinnerNumberModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Entities.TimeInterval; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Exceptions.BackupAlreadyRunningException; +import backupmanager.Exceptions.BackupDeletionException; +import backupmanager.Exceptions.InvalidTimeInterval; +import backupmanager.Helpers.BackupHelper; +import backupmanager.Utils.ToastUtils; +import backupmanager.gui.Controllers.BackupEntryController; +import backupmanager.gui.Table.BackupTableDataService; +import net.miginfocom.swing.MigLayout; +import raven.modal.ModalDialog; +import raven.modal.component.ModalBorderAction; +import raven.modal.component.SimpleModalBorder; +import raven.modal.option.Location; +import raven.modal.option.Option; + +public class BackupEntryDialog extends CustomDialog<ConfigurationBackup> { + + private static final Logger logger = LoggerFactory.getLogger(BackupHelper.class); + private final BackupEntryController entryController; + private final BackupTableDataService backupTable; + + private final boolean create; + private String backupOnText; + private String backupOffText; + + public BackupEntryDialog(BackupTableDataService backupTable) { + entryController = new BackupEntryController(null); + this.backupTable = backupTable; + create = true; + + build(); + + setAutoBackupOff(); + executeBackupBtn.setEnabled(false); + } + + public BackupEntryDialog(BackupTableDataService backupTable, ConfigurationBackup currentBackup) { + entryController = new BackupEntryController(currentBackup); + this.backupTable = backupTable; + create = false; + + build(); + + updateCurrentFiedsByBackup(currentBackup); + txtBackupName.setText(currentBackup.getName()); + txtBackupName.setEditable(false); + txtBackupName.setFocusable(false); + } + + @Override + protected void init() { + setLayout(new MigLayout("fillx,wrap,insets 5 30 5 30,width 400", "[fill]", "")); + txtBackupName = new JTextField(); + txtTargetPath = new JTextField(); + txtDestinationPath = new JTextField(); + executeBackupBtn = new JButton("Execute Backup"); + automaticBackupBtn = new JToggleButton("Automatic Backup (OFF)"); + targetPathBtn = new JButton(new FlatSVGIcon("icons/folder.svg", 25, 25)); + destinationPathBtn = new JButton(new FlatSVGIcon("icons/folder.svg", 25, 25)); + timeIntervalBtn = new JButton(new FlatSVGIcon("icons/timer.svg", 25, 25)); + maxToKeeSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); + maxToKeeLabel = new JLabel("Max to keep"); + lastBackupLabel = new JLabel("Last backup: never"); + txtNotes = new JLabel("Notes"); + textAreaNotes = new JTextArea(); + textAreaNotes.setWrapStyleWord(true); + textAreaNotes.setLineWrap(true); + txtPath = new JLabel("Paths"); + txtBackupNameLabel = new JLabel("Backup Name"); + JScrollPane scroll = new JScrollPane(textAreaNotes); + + createTitle(Translations.get(TKey.PAGE_SUBTITLE_INFO)); + + add(txtBackupNameLabel, "gapy 5 0"); + add(txtBackupName); + add(txtPath, "gapy 5 0"); + add(txtTargetPath, "split 2"); + add(targetPathBtn, "w 30!, h 30!"); + add(txtDestinationPath, "split 2"); + add(destinationPathBtn, "w 30!, h 30!"); + + add(txtNotes, "gapy 5 0"); + add(scroll, "height 120,grow,pushy"); + + createTitle(Translations.get(TKey.PAGE_SUBTITLE_SETTINGS)); + + add(lastBackupLabel); + add(executeBackupBtn); + add(automaticBackupBtn, "split 2"); + add(timeIntervalBtn, "w 30!, h 30!"); + + add(maxToKeeLabel, "gapy 5 0"); + add(maxToKeeSpinner, "width 100"); + + executeBackupBtn.addActionListener(e -> executeBackup()); + automaticBackupBtn.addActionListener(e -> toggleAutomaticBackup()); + targetPathBtn.addActionListener(e -> entryController.openFileChooser(txtTargetPath, true)); + destinationPathBtn.addActionListener(e -> entryController.openFileChooser(txtDestinationPath, false)); + timeIntervalBtn.addActionListener(e -> openTimeInterval()); + + executeBackupBtn.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR)); + automaticBackupBtn.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR)); + targetPathBtn.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR)); + destinationPathBtn.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR)); + timeIntervalBtn.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR)); + + styleSpinner(maxToKeeSpinner); + configureSpinner(maxToKeeSpinner, 1, 100); + + textAreaNotes.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + if (e.isControlDown() && e.getKeyChar() == 10) { + ModalBorderAction modalBorderAction = ModalBorderAction.getModalBorderAction(BackupEntryDialog.this); + if (modalBorderAction != null) { + modalBorderAction.doAction(SimpleModalBorder.YES_OPTION); + } + } + } + }); + } + + @Override + public ConfigurationBackup getResult() { + return entryController.getCurrentBackup(); + } + + private void openTimeInterval() { + TimePickerDialog timePicker = new TimePickerDialog(entryController.getCurrentBackup().getTimeIntervalBackup()); + + Option option = ModalDialog.createOption(); + option.getLayoutOption().setSize(-1, 1f) + .setLocation(Location.TRAILING, Location.TOP) + .setAnimateDistance(0.7f, 0); + ModalDialog.showModal(this, new SimpleModalBorder( + timePicker, + Translations.get(TKey.TIME_INTERVAL_TITLE), + SimpleModalBorder.YES_NO_OPTION, + (controller, action) -> { + if (action == SimpleModalBorder.YES_OPTION) { + + try { + TimeInterval time = entryController.handleTimePickerAction(timePicker, txtTargetPath.getText(), txtDestinationPath.getText()); + timeIntervalBtn.setToolTipText(time.toString()); + openBackupActivationMessage(time); + } catch (InvalidTimeInterval e) { + ToastUtils.showError(this, Translations.get(TKey.TOAST_INVALID_TIME)); + // no actions + } + + controller.close(); + } + + if (action == SimpleModalBorder.NO_OPTION) { + controller.close(); + } + }), option); + } + + private void updateCurrentFiedsByBackup(ConfigurationBackup backup) { + setStartPathField(backup.getTargetPath()); + setDestinationPathField(backup.getDestinationPath()); + setLastBackupLabel(backup.getLastUpdateDate()); + setAutoBackupPreference(backup.isAutomatic()); + setCurrentBackupNotes(backup.getNotes()); + setCurrentBackupMaxBackupsToKeep(backup.getMaxToKeep()); + + if (backup.getTimeIntervalBackup() != null) { + setAutoBackupOn(backup); + } else { + setAutoBackupOff(); + } + } + + private void executeBackup() { + try { + entryController.handleSingleBackupRequest( + backupTable, + txtBackupName.getText(), + txtTargetPath.getText(), + txtDestinationPath.getText(), + textAreaNotes.getText(), + automaticBackupBtn.isSelected(), + (int) maxToKeeSpinner.getValue() + ); + } catch (BackupAlreadyRunningException e) { + ToastUtils.showWarning(this, Translations.get(TKey.TOAST_BACKUP_ALREADY_IN_PROGRESS)); + } + } + + private void toggleAutomaticBackup() { + if (entryController.toggleAutomaticBackup(this, txtBackupName.getText(), txtTargetPath.getText(), txtDestinationPath.getText(), txtNotes.getText(), automaticBackupBtn.isSelected(), (int) maxToKeeSpinner.getValue())) { + setAutoBackupOn(entryController.getCurrentBackup()); + automaticBackupBtn.setSelected(true); + timeIntervalBtn.setToolTipText(entryController.getCurrentBackup().getTimeIntervalBackup().toString()); + timeIntervalBtn.setEnabled(true); + } else { + setAutoBackupOff(); + automaticBackupBtn.setSelected(false); + } + } + + private void setAutoBackupOn(ConfigurationBackup backup) { + automaticBackupBtn.setSelected(true); + automaticBackupBtn.setText(backupOnText); + + if (backup != null) + enableTimePickerButton(backup); + else + disableTimePickerButton(); + } + + private void setAutoBackupOff() { + automaticBackupBtn.setSelected(false); + automaticBackupBtn.setText(backupOffText); + disableTimePickerButton(); + } + + private void disableTimePickerButton() { + timeIntervalBtn.setToolTipText(Translations.get(TKey.TIME_PICKER_TOOLTIP)); + timeIntervalBtn.setEnabled(false); + } + + private void enableTimePickerButton(ConfigurationBackup backup) { + if (backup.getTimeIntervalBackup() != null) { + timeIntervalBtn.setToolTipText(backup.getTimeIntervalBackup().toString()); + timeIntervalBtn.setEnabled(true); + } else { + timeIntervalBtn.setEnabled(true); + } + } + + public boolean canDispose() { + try { + return entryController.canDisposeAfterOk(this, txtBackupName.getText(), txtTargetPath.getText(), txtDestinationPath.getText(), textAreaNotes.getText(), automaticBackupBtn.isSelected(), (int) maxToKeeSpinner.getValue(), create); + } catch (BackupDeletionException e) { + ToastUtils.showError(this, Translations.get(TKey.TOAST_BACKUP_REPLACED_ERROR)); + return false; + } + } + + private void disableAutoBackup(ConfigurationBackup backup) { + logger.info("Event --> auto backup disabled"); + + backup.setTimeIntervalBackup(null); + backup.setNextBackupDate(null); + backup.setAutomatic(false); + backup.setLastUpdateDate(LocalDateTime.now()); + } + + private void openBackupActivationMessage(TimeInterval newtimeInterval) { + entryController.handleOpenBackupActivationMessage(this, newtimeInterval, txtTargetPath.getText(), txtDestinationPath.getText()); + } + + private void setAutoBackupPreference(boolean option) { + ConfigurationBackup currentBackup = entryController.getCurrentBackup(); + currentBackup.setAutomatic(option); + + if (option) { + setAutoBackupOn(currentBackup); + } else { + disableAutoBackup(currentBackup); + } + } + + private void setLastBackupLabel(LocalDateTime date) { + if (date != null) { + String dateStr = date.format(BackupHelper.formatter); + dateStr = Translations.get(TKey.LAST_BACKUP) + ": " + dateStr; + lastBackupLabel.setText(dateStr); + } + else lastBackupLabel.setText(""); + } + + private void setStartPathField(String text) { + txtTargetPath.setText(text); + } + private void setDestinationPathField(String text) { + txtDestinationPath.setText(text); + } + private void setCurrentBackupNotes(String notes) { + textAreaNotes.setText(notes); + } + private void setCurrentBackupMaxBackupsToKeep(int maxBackupsCount) { + maxToKeeSpinner.setValue(maxBackupsCount); + } + + @Override + public void setTranslations() { + backupOnText = Translations.get(TKey.AUTO_BACKUP_BUTTON_ON); + backupOffText = Translations.get(TKey.AUTO_BACKUP_BUTTON_OFF); + targetPathBtn.setToolTipText(Translations.get(TKey.INITIAL_FILE_CHOOSER_TOOLTIP)); + destinationPathBtn.setToolTipText(Translations.get(TKey.DESTINATION_FILE_CHOOSER_TOOLTIP)); + txtTargetPath.setToolTipText(Translations.get(TKey.INITIAL_PATH_TOOLTIP)); + txtDestinationPath.setToolTipText(Translations.get(TKey.DESTINATION_PATH_TOOLTIP)); + textAreaNotes.setToolTipText(Translations.get(TKey.NOTES_TOOLTIP)); + executeBackupBtn.setText(Translations.get(TKey.SINGLE_BACKUP_BUTTON)); + executeBackupBtn.setToolTipText(Translations.get(TKey.SINGLE_BACKUP_TOOLTIP)); + automaticBackupBtn.setText(Translations.get(TKey.AUTO_BACKUP_BUTTON_OFF)); + automaticBackupBtn.setToolTipText(Translations.get(TKey.AUTO_BACKUP_TOOLTIP)); + txtNotes.setText(Translations.get(TKey.NOTES) + ":"); + lastBackupLabel.setText(Translations.get(TKey.LAST_BACKUP) + ": "); + txtTargetPath.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.INITIAL_PATH_PLACEHOLDER)); + txtDestinationPath.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.DESTINATION_PATH_PLACEHOLDER)); + timeIntervalBtn.setToolTipText(Translations.get(TKey.TIME_PICKER_TOOLTIP)); + maxToKeeSpinner.setToolTipText(Translations.get(TKey.MAX_BACKUPS_TO_KEEP_TOOLTIP) + "\n" + Translations.get(TKey.SPINNER_TOOLTIP)); + maxToKeeLabel.setText(Translations.get(TKey.MAX_BACKUPS_TO_KEEP)); + txtBackupName.setToolTipText(Translations.get(TKey.BACKUP_NAME_TOOLTIP)); + txtBackupName.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, Translations.get(TKey.BACKUP_NAME_PLACEHOLDER)); + txtPath.setText(Translations.get(TKey.PATHS)); + txtBackupNameLabel.setText(Translations.get(TKey.BACKUP_NAME)); + } + + private JTextField txtBackupName; + private JTextField txtTargetPath; + private JTextField txtDestinationPath; + private JLabel txtBackupNameLabel; + private JLabel txtPath; + private JLabel txtNotes; + private JTextArea textAreaNotes; + private JLabel lastBackupLabel; + private JButton executeBackupBtn; + private JToggleButton automaticBackupBtn; + private JButton targetPathBtn; + private JButton destinationPathBtn; + private JButton timeIntervalBtn; + private JSpinner maxToKeeSpinner; + private JLabel maxToKeeLabel; +} diff --git a/src/main/java/backupmanager/gui/simple/CustomDialog.java b/src/main/java/backupmanager/gui/simple/CustomDialog.java new file mode 100644 index 00000000..1bf2f61c --- /dev/null +++ b/src/main/java/backupmanager/gui/simple/CustomDialog.java @@ -0,0 +1,58 @@ +package backupmanager.gui.simple; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JSpinner; + +import com.formdev.flatlaf.FlatClientProperties; + +public abstract class CustomDialog<T> extends JPanel { + + public CustomDialog() { } + + protected final void build() { + init(); + setTranslations(); + } + + protected abstract void init(); + protected abstract void setTranslations(); + protected abstract T getResult(); + + protected void createTitle(String title) { + JLabel lb = new JLabel(title); + lb.putClientProperty(FlatClientProperties.STYLE, "font:+2"); + add(lb, "gapy 5 0"); + add(new JSeparator(), "height 2!,gapy 0 0"); + } + + protected void styleSpinner(JSpinner spinner) { + spinner.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:10;" + + "minimumWidth:90"); + } + + protected void configureSpinner(JSpinner spinner, int min, int max) { + if (spinner.getEditor() instanceof JSpinner.NumberEditor editor) { + editor.getTextField().setEditable(false); + } + + spinner.addMouseWheelListener(evt -> { + int rotation = evt.getWheelRotation(); + int current = (Integer) spinner.getValue(); + spinner.setValue(current + (rotation < 0 ? 1 : -1)); + validateSpinner(spinner, min, max); + }); + } + + private void validateSpinner(JSpinner spinner, int min, int max) { + Integer value = (Integer) spinner.getValue(); + + if (value == null || value < min) { + spinner.setValue(min); + } else if (value > max) { + spinner.setValue(max); + } + } +} diff --git a/src/main/java/backupmanager/gui/simple/TimePickerDialog.java b/src/main/java/backupmanager/gui/simple/TimePickerDialog.java new file mode 100644 index 00000000..86a184cb --- /dev/null +++ b/src/main/java/backupmanager/gui/simple/TimePickerDialog.java @@ -0,0 +1,104 @@ +package backupmanager.gui.simple; + +import javax.swing.JLabel; +import javax.swing.JSpinner; +import javax.swing.JTextArea; +import javax.swing.SpinnerNumberModel; + +import com.formdev.flatlaf.FlatClientProperties; + +import backupmanager.Entities.TimeInterval; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.gui.Controllers.TimePickerController; +import net.miginfocom.swing.MigLayout; + +public class TimePickerDialog extends CustomDialog<TimeInterval> { + + private final TimePickerController timePickerController; + + public TimePickerDialog(TimeInterval timeInterval) { + build(); + + timePickerController = new TimePickerController(); + + if (timeInterval != null) { + daysSpinner.setValue(timeInterval.days()); + hoursSpinner.setValue(timeInterval.hours()); + minutesSpinner.setValue(timeInterval.minutes()); + } + } + + @Override + protected void init() { + + setLayout(new MigLayout( + "fillx,wrap 2,insets 20 35 20 35,width 420", + "[right][grow,fill]", + "" + )); + + description = new JTextArea(); + description.setEditable(false); + description.setOpaque(false); + description.setWrapStyleWord(true); + description.setLineWrap(true); + description.setFocusable(false); + description.putClientProperty(FlatClientProperties.STYLE, "foreground:$Label.disabledForeground"); + + add(description, "span,growx,gapy 0 15"); + + daysLabel = new JLabel(); + hoursLabel = new JLabel(); + minutesLabel = new JLabel(); + + daysSpinner = new JSpinner(new SpinnerNumberModel(30, 0, 365, 1)); + hoursSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 23, 1)); + minutesSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 59, 1)); + + styleSpinner(daysSpinner); + styleSpinner(hoursSpinner); + styleSpinner(minutesSpinner); + + configureSpinner(daysSpinner, 0, 365); + configureSpinner(hoursSpinner, 0, 23); + configureSpinner(minutesSpinner, 0, 59); + + add(daysLabel, "gapy 5 0"); + add(daysSpinner, "width 90!"); + + add(hoursLabel, "gapy 5 0"); + add(hoursSpinner, "width 90!"); + + add(minutesLabel, "gapy 5 0"); + add(minutesSpinner, "width 90!"); + } + + @Override + public TimeInterval getResult() { + int days = (Integer) daysSpinner.getValue(); + int hours = (Integer) hoursSpinner.getValue(); + int minutes = (Integer) minutesSpinner.getValue(); + + return timePickerController.getTimeIntervalIfPossible(this, days, hours, minutes); + } + + @Override + public void setTranslations() { + description.setText(Translations.get(TKey.DESCRIPTION)); + daysSpinner.setToolTipText(Translations.get(TKey.SPINNER_TOOLTIP)); + hoursSpinner.setToolTipText(Translations.get(TKey.SPINNER_TOOLTIP)); + minutesSpinner.setToolTipText(Translations.get(TKey.SPINNER_TOOLTIP)); + daysLabel.setText(Translations.get(TKey.DAYS)); + hoursLabel.setText(Translations.get(TKey.HOURS)); + minutesLabel.setText(Translations.get(TKey.MINUTES)); + } + + private JTextArea description; + private JLabel daysLabel; + private JLabel hoursLabel; + private JLabel minutesLabel; + private JSpinner daysSpinner; + private JSpinner hoursSpinner; + private JSpinner minutesSpinner; +} diff --git a/src/main/java/backupmanager/svg/SVGButton.java b/src/main/java/backupmanager/gui/svg/SVGButton.java similarity index 75% rename from src/main/java/backupmanager/svg/SVGButton.java rename to src/main/java/backupmanager/gui/svg/SVGButton.java index b95b78d3..0d6523c2 100644 --- a/src/main/java/backupmanager/svg/SVGButton.java +++ b/src/main/java/backupmanager/gui/svg/SVGButton.java @@ -1,4 +1,4 @@ -package backupmanager.svg; +package backupmanager.gui.svg; import java.awt.Cursor; @@ -12,6 +12,11 @@ public SVGButton() { setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } + public SVGButton(String text) { + super(text); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + public void setSvgImage(String imagePath, int width, int height) { if (imagePath == null) return; diff --git a/src/main/java/backupmanager/gui/svg/SVGIconUIColor.java b/src/main/java/backupmanager/gui/svg/SVGIconUIColor.java new file mode 100644 index 00000000..9832a831 --- /dev/null +++ b/src/main/java/backupmanager/gui/svg/SVGIconUIColor.java @@ -0,0 +1,42 @@ +package backupmanager.gui.svg; + +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.util.ColorFunctions; + +import javax.swing.*; +import java.awt.*; + +public class SVGIconUIColor extends FlatSVGIcon { + + private String colorKey; + private float alpha; + + public SVGIconUIColor(String name, float scale, String colorKey, float alpha) { + super(name, scale); + this.colorKey = colorKey; + this.alpha = alpha; + setColorFilter(new ColorFilter(color -> { + Color uiColor = UIManager.getColor(getColorKey()); + if (uiColor != null) { + return getAlpha() == 1 ? uiColor : ColorFunctions.fade(uiColor, getAlpha()); + } + return color; + })); + } + + public String getColorKey() { + return colorKey; + } + + public void setColorKey(String colorKey) { + this.colorKey = colorKey; + } + + public float getAlpha() { + return alpha; + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + } +} diff --git a/src/main/java/backupmanager/svg/SVGManager.java b/src/main/java/backupmanager/gui/svg/SVGManager.java similarity index 97% rename from src/main/java/backupmanager/svg/SVGManager.java rename to src/main/java/backupmanager/gui/svg/SVGManager.java index 275917f1..dce20c3e 100644 --- a/src/main/java/backupmanager/svg/SVGManager.java +++ b/src/main/java/backupmanager/gui/svg/SVGManager.java @@ -1,4 +1,4 @@ -package backupmanager.svg; +package backupmanager.gui.svg; import java.awt.Color; import java.awt.Component; diff --git a/src/main/java/backupmanager/svg/SVGMenu.java b/src/main/java/backupmanager/gui/svg/SVGMenu.java similarity index 92% rename from src/main/java/backupmanager/svg/SVGMenu.java rename to src/main/java/backupmanager/gui/svg/SVGMenu.java index ab928a68..4c5bd6b4 100644 --- a/src/main/java/backupmanager/svg/SVGMenu.java +++ b/src/main/java/backupmanager/gui/svg/SVGMenu.java @@ -1,4 +1,4 @@ -package backupmanager.svg; +package backupmanager.gui.svg; import com.formdev.flatlaf.extras.FlatSVGIcon; import javax.swing.JMenu; diff --git a/src/main/java/backupmanager/svg/SVGMenuItem.java b/src/main/java/backupmanager/gui/svg/SVGMenuItem.java similarity index 92% rename from src/main/java/backupmanager/svg/SVGMenuItem.java rename to src/main/java/backupmanager/gui/svg/SVGMenuItem.java index c49abfc1..785a2c94 100644 --- a/src/main/java/backupmanager/svg/SVGMenuItem.java +++ b/src/main/java/backupmanager/gui/svg/SVGMenuItem.java @@ -1,4 +1,4 @@ -package backupmanager.svg; +package backupmanager.gui.svg; import javax.swing.JMenuItem; diff --git a/src/main/java/backupmanager/gui/system/AllForms.java b/src/main/java/backupmanager/gui/system/AllForms.java new file mode 100644 index 00000000..3c2c432f --- /dev/null +++ b/src/main/java/backupmanager/gui/system/AllForms.java @@ -0,0 +1,44 @@ +package backupmanager.gui.system; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.SwingUtilities; + +public class AllForms { + + private static AllForms instance; + + private final Map<Class<? extends Form>, Form> formsMap; + + private static AllForms getInstance() { + if (instance == null) { + instance = new AllForms(); + } + return instance; + } + + private AllForms() { + formsMap = new HashMap<>(); + } + + public static Form getForm(Class<? extends Form> cls) { + if (getInstance().formsMap.containsKey(cls)) { + return getInstance().formsMap.get(cls); + } + try { + Form form = cls.getDeclaredConstructor().newInstance(); + getInstance().formsMap.put(cls, form); + formInit(form); + return form; + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | + IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void formInit(Form form) { + SwingUtilities.invokeLater(() -> form.formInit()); + } +} diff --git a/src/main/java/backupmanager/gui/system/Form.java b/src/main/java/backupmanager/gui/system/Form.java new file mode 100644 index 00000000..a5e3f871 --- /dev/null +++ b/src/main/java/backupmanager/gui/system/Form.java @@ -0,0 +1,32 @@ +package backupmanager.gui.system; + +import javax.swing.JPanel; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public abstract class Form extends JPanel { + + private LookAndFeel oldTheme = UIManager.getLookAndFeel(); + + public Form() { + init(); + } + + private void init() { } + + public abstract void formInit(); + + public void formOpen() { } + + public abstract void formRefresh(); + + protected boolean formCheck() { + if (oldTheme != UIManager.getLookAndFeel()) { + oldTheme = UIManager.getLookAndFeel(); + SwingUtilities.updateComponentTreeUI(this); + return true; + } + return false; + } +} diff --git a/src/main/java/backupmanager/gui/system/FormManager.java b/src/main/java/backupmanager/gui/system/FormManager.java new file mode 100644 index 00000000..129985a6 --- /dev/null +++ b/src/main/java/backupmanager/gui/system/FormManager.java @@ -0,0 +1,151 @@ +package backupmanager.gui.system; + +import javax.swing.JFrame; + +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.util.ColorFunctions; + +import backupmanager.Enums.SubscriptionStatus; +import backupmanager.Enums.Translations; +import backupmanager.Enums.Translations.TKey; +import backupmanager.Helpers.SubscriptionHelper; +import backupmanager.Helpers.SubscriptionNotifier; +import backupmanager.Utils.ToastUtils; +import backupmanager.Utils.UndoRedo; +import backupmanager.gui.component.About; +import backupmanager.gui.component.Subscription; +import backupmanager.gui.forms.FormBackupTable; +import backupmanager.gui.forms.FormLogin; +import raven.modal.Drawer; +import raven.modal.ModalDialog; +import raven.modal.component.SimpleModalBorder; + +public class FormManager { + + protected static final UndoRedo<Form> FORMS = new UndoRedo<>(); + private static JFrame frame; + private static MainForm mainForm; + private static FormLogin login; + + public static void install(JFrame f) { + frame = f; + install(); + logout(); + } + + private static void install() { + FormSearch.getInstance().installKeyMap(getMainForm()); + FlatSVGIcon.ColorFilter.getInstance().setMapperEx((component, color) -> { + if (color.getRGB() == -6908266) { + return FlatLaf.isLafDark() ? ColorFunctions.shade(component.getForeground(), 0.2f) + : ColorFunctions.tint(component.getForeground(), 0.4f); + } + return color; + }); + } + + public static void showForm(Form form) { + Form current = FORMS.getCurrent(); + if (form != current) { + FORMS.add(form); + form.formCheck(); + form.formOpen(); + mainForm.setForm(form); + mainForm.refresh(); + } + } + + public static void undo() { + if (FORMS.isUndoAble()) { + Form form = FORMS.undo(); + form.formCheck(); + form.formOpen(); + mainForm.setForm(form); + Drawer.setSelectedItemClass(form.getClass()); + } + } + + public static void redo() { + if (FORMS.isRedoAble()) { + Form form = FORMS.redo(); + form.formCheck(); + form.formOpen(); + mainForm.setForm(form); + Drawer.setSelectedItemClass(form.getClass()); + } + } + + public static void refresh() { + if (FORMS.getCurrent() != null) { + FORMS.getCurrent().formRefresh(); + mainForm.refresh(); + } + } + + public static void login() { + Drawer.setVisible(true); + frame.getContentPane().removeAll(); + frame.getContentPane().add(getMainForm()); + + Drawer.setSelectedItemClass(FormBackupTable.class); + showSubscriptionAlertIfNeeded(); + frame.repaint(); + frame.revalidate(); + } + + public static void logout() { + Drawer.setVisible(false); + frame.getContentPane().removeAll(); + Form login = getLogin(); + login.formCheck(); + frame.getContentPane().add(login); + FORMS.clear(); + frame.repaint(); + frame.revalidate(); + } + + public static JFrame getFrame() { + return frame; + } + + private static MainForm getMainForm() { + if (mainForm == null) { + mainForm = new MainForm(); + } + return mainForm; + } + + private static FormLogin getLogin() { + if (login == null) { + login = new FormLogin(); + } + return login; + } + + public static void showAbout() { + ModalDialog.showModal(frame, new SimpleModalBorder(new About(), Translations.get(TKey.ABOUT)), + ModalDialog.createOption().setAnimationEnabled(false) + ); + } + + public static void showSubscription() { + ModalDialog.showModal(frame, new SimpleModalBorder(new Subscription(), Translations.get(TKey.SUBSCRIPTION)), + ModalDialog.createOption().setAnimationEnabled(false) + ); + } + + private static void showSubscriptionAlertIfNeeded() { + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + + switch (status) { + case SubscriptionStatus.EXPIRATION -> { + ToastUtils.showWarning(frame, Translations.get(TKey.TOAST_SUBSCRIPTION_EXPIRING)); + } + case SubscriptionStatus.EXPIRED -> { + ToastUtils.showError(frame, Translations.get(TKey.TOAST_SUBSCRIPTION_EXPIRED)); + } + case ACTIVE, NONE -> { } + } + } +} diff --git a/src/main/java/backupmanager/gui/system/FormSearch.java b/src/main/java/backupmanager/gui/system/FormSearch.java new file mode 100644 index 00000000..03626766 --- /dev/null +++ b/src/main/java/backupmanager/gui/system/FormSearch.java @@ -0,0 +1,101 @@ +package backupmanager.gui.system; + +import java.awt.ComponentOrientation; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + +import backupmanager.gui.component.EmptyModalBorder; +import backupmanager.gui.component.FormSearchPanel; +import backupmanager.gui.menu.DrawerManager; +import backupmanager.Utils.SystemForm; +import raven.modal.ModalDialog; +import raven.modal.drawer.item.Item; +import raven.modal.drawer.item.MenuItem; +import raven.modal.option.Location; +import raven.modal.option.Option; + +public class FormSearch { + + private static FormSearch instance; + public static final String ID = "search"; + private final Map<SystemForm, Class<? extends Form>> formsMap; + private FormSearchPanel searchPanel; + + public static FormSearch getInstance() { + if (instance == null) { + instance = new FormSearch(); + } + return instance; + } + + private FormSearch() { + formsMap = new HashMap<>(); + for (Class<? extends Form> cls : getClassForms()) { + if (cls.isAnnotationPresent(SystemForm.class)) { + SystemForm f = cls.getAnnotation(SystemForm.class); + formsMap.put(f, cls); + } + } + } + + private Class<? extends Form>[] getClassForms() { + MenuItem[] menuItems = DrawerManager.getInstance().getMenuItems(); + List<Class<?>> formClass = new ArrayList<>(); + getMenuClass(menuItems, formClass); + return formClass.toArray(new Class[0]); + } + + private void getMenuClass(MenuItem[] menuItems, List<Class<?>> formClass) { + for (MenuItem menu : menuItems) { + if (menu.isMenu()) { + Item item = (Item) menu; + if (item.getItemClass() != null) { + formClass.add(item.getItemClass()); + } + if (item.isSubmenuAble()) { + getMenuClass(item.getSubMenu().toArray(new Item[0]), formClass); + } + } + } + } + + public void installKeyMap(JComponent component) { + ActionListener key = e -> showSearch(); + component.registerKeyboardAction(key, KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); + } + + public void showSearch() { + if (ModalDialog.isIdExist(ID)) { + return; + } + Option option = ModalDialog.createOption(); + option.setAnimationEnabled(false); + option.getLayoutOption().setMargin(20, 10, 10, 10).setLocation(Location.CENTER, Location.TOP); + ModalDialog.showModal(FormManager.getFrame(), new EmptyModalBorder(getSearchPanel(), (controller, action) -> { + if (action == EmptyModalBorder.OPENED) { + searchPanel.searchGrabFocus(); + } + }), option, ID); + } + + private JPanel getSearchPanel() { + if (searchPanel == null) { + searchPanel = new FormSearchPanel(formsMap); + } + searchPanel.formCheck(); + searchPanel.clearSearch(); + ComponentOrientation orientation = FormManager.getFrame().getComponentOrientation(); + if (orientation.isLeftToRight() != searchPanel.getComponentOrientation().isLeftToRight()) { + searchPanel.applyComponentOrientation(orientation); + } + return searchPanel; + } +} diff --git a/src/main/java/backupmanager/gui/system/MainForm.java b/src/main/java/backupmanager/gui/system/MainForm.java new file mode 100644 index 00000000..bfcde352 --- /dev/null +++ b/src/main/java/backupmanager/gui/system/MainForm.java @@ -0,0 +1,134 @@ +package backupmanager.gui.system; + +import java.awt.BorderLayout; +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JToolBar; + +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; + +import backupmanager.Enums.ConfigKey; +import backupmanager.gui.component.FormSearchButton; +import backupmanager.gui.component.RefreshLine; +import net.miginfocom.swing.MigLayout; +import raven.modal.Drawer; + +public class MainForm extends JPanel { + + public MainForm() { + init(); + } + + private void init() { + setLayout(new MigLayout( + "fillx,wrap,insets 0,gap 0", + "[fill]", + "[][3!][fill,grow][2!][]" + )); + add(createHeader()); + add(createRefreshLine(), "height 3!"); + add(createMain()); + add(new JSeparator(), "height 2!"); + add(createFooter()); + } + + private JPanel createHeader() { + JPanel panel = new JPanel(new MigLayout("insets 3", "[]push[]push", "[fill]")); + JToolBar toolBar = new JToolBar(); + JButton buttonDrawer = new JButton(new FlatSVGIcon("icons/menu.svg", 0.5f)); + buttonUndo = new JButton(new FlatSVGIcon("icons/undo.svg", 0.5f)); + buttonRedo = new JButton(new FlatSVGIcon("icons/redo.svg", 0.5f)); + buttonRefresh = new JButton(new FlatSVGIcon("icons/refresh.svg", 0.5f)); + + // style + buttonDrawer.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:10;"); + buttonUndo.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:10;"); + buttonRedo.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:10;"); + buttonRefresh.putClientProperty(FlatClientProperties.STYLE, "" + + "arc:10;"); + + buttonDrawer.addActionListener(e -> { + if (Drawer.isOpen()) { + Drawer.showDrawer(); + } else { + Drawer.toggleMenuOpenMode(); + } + }); + buttonUndo.addActionListener(e -> FormManager.undo()); + buttonRedo.addActionListener(e -> FormManager.redo()); + buttonRefresh.addActionListener(e -> FormManager.refresh()); + + toolBar.add(buttonDrawer); + toolBar.add(buttonUndo); + toolBar.add(buttonRedo); + toolBar.add(buttonRefresh); + panel.add(toolBar); + panel.add(createSearchBox(), "gapx n 135"); + return panel; + } + + private JPanel createFooter() { + JPanel panel = new JPanel(new MigLayout("insets 1 n 1 n,al trailing center,gapx 10,height 30!", "[]push[][]", "fill")); + panel.putClientProperty(FlatClientProperties.STYLE, "background:$Menu.background;"); + + // demo version + JLabel version = new JLabel("Backup Manager: v" + ConfigKey.VERSION.getValue()); + version.putClientProperty(FlatClientProperties.STYLE, "" + + "foreground:$Label.disabledForeground;"); + panel.add(version); + + return panel; + } + + private JPanel createSearchBox() { + JPanel panel = new JPanel(new MigLayout("fill", "[fill,center,200:250:]", "[fill]")); + FormSearchButton button = new FormSearchButton(); + button.addActionListener(e -> FormSearch.getInstance().showSearch()); + panel.add(button); + return panel; + } + + private JPanel createRefreshLine() { + refreshLine = new RefreshLine(); + return refreshLine; + } + + private Component createMain() { + mainPanel = new JPanel(new BorderLayout()); + return mainPanel; + } + + public void setForm(Form form) { + mainPanel.removeAll(); + mainPanel.add(form); + mainPanel.repaint(); + mainPanel.revalidate(); + + // check button + buttonUndo.setEnabled(FormManager.FORMS.isUndoAble()); + buttonRedo.setEnabled(FormManager.FORMS.isRedoAble()); + // check component orientation and update + if (mainPanel.getComponentOrientation().isLeftToRight() != form.getComponentOrientation().isLeftToRight()) { + applyComponentOrientation(mainPanel.getComponentOrientation()); + } + } + + public void refresh() { + refreshLine.refresh(); + } + + private JPanel mainPanel; + private RefreshLine refreshLine; + + private JButton buttonUndo; + private JButton buttonRedo; + private JButton buttonRefresh; +} diff --git a/src/main/java/backupmanager/gui/themes/ListCellTitledBorder.java b/src/main/java/backupmanager/gui/themes/ListCellTitledBorder.java new file mode 100644 index 00000000..0b2d6515 --- /dev/null +++ b/src/main/java/backupmanager/gui/themes/ListCellTitledBorder.java @@ -0,0 +1,74 @@ +package backupmanager.gui.themes; + +import java.awt.Component; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.geom.Rectangle2D; + +import javax.swing.JList; +import javax.swing.UIManager; +import javax.swing.border.Border; + +import com.formdev.flatlaf.ui.FlatUIUtils; +import com.formdev.flatlaf.util.UIScale; + +public class ListCellTitledBorder<T> implements Border { + + private final JList<T> list; + private final String title; + + ListCellTitledBorder(JList<T> list, String title) { + this.list = list; + this.title = title; + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + FontMetrics fm = c.getFontMetrics(list.getFont()); + int titleWidth = fm.stringWidth(title); + int titleHeight = fm.getHeight(); + + // fill background + g.setColor(list.getBackground()); + g.fillRect(x, y, width, titleHeight); + + int gap = UIScale.scale(4); + Graphics2D g2 = (Graphics2D) g.create(); + + try { + FlatUIUtils.setRenderingHints(g2); + g2.setColor(UIManager.getColor("Label.disabledForeground")); + + // paint separator line + int sepWidth = (width - titleWidth) / 2 - (gap * 2); + if (sepWidth > 0) { + int sy = y + Math.round(titleHeight / 2f); + float sepHeight = UIScale.scale(1f); + + g2.fill(new Rectangle2D.Float(x + gap, sy, sepWidth, sepHeight)); + g2.fill(new Rectangle2D.Float(x + width - gap - sepWidth, sy, sepWidth, sepHeight)); + } + + // draw title + int xt = x + ((width - titleWidth) / 2); + int yt = y + fm.getAscent(); + + FlatUIUtils.drawString(list, g2, title, xt, yt); + } finally { + g2.dispose(); + } + } + + @Override + public Insets getBorderInsets(Component c) { + int height = c.getFontMetrics(list.getFont()).getHeight(); + return new Insets(height, 0, 0, 0); + } + + @Override + public boolean isBorderOpaque() { + return true; + } +} diff --git a/src/main/java/backupmanager/gui/themes/PanelThemes.java b/src/main/java/backupmanager/gui/themes/PanelThemes.java new file mode 100644 index 00000000..58737fc6 --- /dev/null +++ b/src/main/java/backupmanager/gui/themes/PanelThemes.java @@ -0,0 +1,209 @@ +package backupmanager.gui.themes; + +import com.formdev.flatlaf.*; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; +import com.formdev.flatlaf.util.LoggingFacade; +import net.miginfocom.swing.MigLayout; +import backupmanager.Utils.AppPreferences; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.event.ListSelectionEvent; +import java.awt.*; +import java.util.*; +import java.util.List; + +public class PanelThemes extends JPanel { + + public static final String THEMES_PACKAGE = "/com/formdev/flatlaf/intellijthemes/themes/"; + private final ThemesManager themesManager = new ThemesManager(); + private final Map<Integer, String> categories = new HashMap<>(); + private final List<ThemesInfo> themes = new ArrayList<>(); + + public PanelThemes() { + init(); + } + + private void init() { + setLayout(new MigLayout("fillx,insets 3", "[fill]", "[fill,grow]")); + themesList = new JList<>(); + themesList.setCellRenderer(new DefaultListCellRenderer() { + + private int index; + private boolean isSelected; + private int titleHeight; + + @Override + public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + this.index = index; + this.isSelected = isSelected; + this.titleHeight = 0; + + String title = categories.get(index); + String name = ((ThemesInfo) value).name(); + int sep = name.indexOf('/'); + if (sep >= 0) { + name = name.substring(sep + 1).trim(); + } + + JComponent com = (JComponent) super.getListCellRendererComponent(list, name, index, isSelected, cellHasFocus); + com.setToolTipText(buildToolTip((ThemesInfo) value)); + if (title != null) { + Border titBorder = new ListCellTitledBorder<>(themesList, title); + com.setBorder(new CompoundBorder(titBorder, com.getBorder())); + this.titleHeight = titBorder.getBorderInsets(com).top; + } + return com; + } + + private String buildToolTip(ThemesInfo th) { + if (th.resourceName() == null) { + return th.name(); + } + return "Name :" + th.name() + + "\nLicense: " + th.license() + + "\nSource Code: " + th.sourceCodeUrl(); + } + }); + themesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + themesList.addListSelectionListener(e -> themesListValueChanged(e)); + JScrollPane scrollPane = new JScrollPane(themesList); + add(scrollPane); + + themesManager.loadThemes(); + updateThemesList(0); + } + + public void updateThemesList(int option) { + boolean showLight = option != 2; + boolean showDark = option != 1; + + ThemesInfo oldSelected = themesList.getSelectedValue(); + + categories.clear(); + themes.clear(); + + // add core themes + categories.put(themes.size(), "Core Themes"); + for (ThemesInfo th : themesManager.coreThemes) { + boolean show = (showLight && !th.dark()) || (showDark && th.dark()); + if (show && !th.name().contains("/")) { + themes.add(th); + } + } + + // add uncategorized bundled themes + categories.put(themes.size(), "IntelliJ Themes"); + for (ThemesInfo th : themesManager.bundledThemes) { + boolean show = (showLight && !th.dark()) || (showDark && th.dark()); + if (show && !th.name().contains("/")) { + themes.add(th); + } + } + + // add categorized bundled themes + String lastCategory = null; + for (ThemesInfo th : themesManager.bundledThemes) { + boolean show = (showLight && !th.dark()) || (showDark && th.dark()); + int sep = th.name().indexOf('/'); + if (!show || sep < 0) { + continue; + } + String category = th.name().substring(0, sep).trim(); + if (!Objects.equals(lastCategory, category)) { + lastCategory = category; + categories.put(themes.size(), category); + } + themes.add(th); + } + + // add themes list model + themesList.setModel(new AbstractListModel<ThemesInfo>() { + @Override + public int getSize() { + return themes.size(); + } + + @Override + public ThemesInfo getElementAt(int index) { + return themes.get(index); + } + }); + + // restore selection + if (oldSelected != null) { + themesList.setSelectedValue(oldSelected, true); + if (themesList.isSelectionEmpty()) { + themesList.setSelectedIndex(0); + } + } else { + selectedCurrentLookAndFeel(); + } + } + + private void selectedCurrentLookAndFeel() { + LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); + String theme = UIManager.getLookAndFeelDefaults().getString(AppPreferences.THEME_UI_KEY); + String lafClassName = lookAndFeel.getClass().getName(); + for (int i = 0; i < themes.size(); i++) { + ThemesInfo ti = themes.get(i); + if (theme == null && ti.lafClassName() != null && lafClassName.equals(ti.lafClassName())) { + themesList.setSelectedIndex(i); + break; + } + if (theme != null && ti.resourceName() != null && theme.substring(AppPreferences.RESOURCE_PREFIX.length()).equals(ti.resourceName())) { + themesList.setSelectedIndex(i); + break; + } + } + } + + private void themesListValueChanged(ListSelectionEvent e) { + ThemesInfo themesInfo = themesList.getSelectedValue(); + boolean bundledTheme = (themesInfo != null && themesInfo.resourceName() != null); + if (e.getValueIsAdjusting()) { + return; + } + EventQueue.invokeLater(() -> setThemes(themesInfo)); + } + + private void setThemes(ThemesInfo themesInfo) { + if (themesInfo == null) { + return; + } + + // change look and feel + if (themesInfo.lafClassName() != null) { + if (themesInfo.lafClassName().equals(UIManager.getLookAndFeel().getClass().getName())) { + return; + } + FlatAnimatedLafChange.showSnapshot(); + try { + UIManager.setLookAndFeel(themesInfo.lafClassName()); + } catch (Exception e) { + LoggingFacade.INSTANCE.logSevere(null, e); + showInformationDialog("Failed to create '" + themesInfo.lafClassName() + "'.", e); + } + } else { + String theme = UIManager.getLookAndFeelDefaults().getString(AppPreferences.THEME_UI_KEY); + if (theme != null && themesInfo.resourceName().equals(theme.substring(AppPreferences.RESOURCE_PREFIX.length()))) { + return; + } + FlatAnimatedLafChange.showSnapshot(); + IntelliJTheme.setup(getClass().getResourceAsStream(THEMES_PACKAGE + themesInfo.resourceName())); + AppPreferences.getState().put(AppPreferences.KEY_LAF_THEME, AppPreferences.RESOURCE_PREFIX + themesInfo.resourceName()); + } + FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); + } + + private void showInformationDialog(String message, Exception e) { + JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(this), + message + "\n\n" + e.getMessage(), + "Message", + JOptionPane.INFORMATION_MESSAGE); + } + + private JList<ThemesInfo> themesList; +} diff --git a/src/main/java/backupmanager/gui/themes/ThemesInfo.java b/src/main/java/backupmanager/gui/themes/ThemesInfo.java new file mode 100644 index 00000000..728b8049 --- /dev/null +++ b/src/main/java/backupmanager/gui/themes/ThemesInfo.java @@ -0,0 +1,12 @@ +package backupmanager.gui.themes; + +public record ThemesInfo ( + String name, + String resourceName, + boolean dark, + String license, + String licenseFile, + String sourceCodeUrl, + String sourceCodePath, + String lafClassName +) { } diff --git a/src/main/java/backupmanager/gui/themes/ThemesManager.java b/src/main/java/backupmanager/gui/themes/ThemesManager.java new file mode 100644 index 00000000..c9321c6f --- /dev/null +++ b/src/main/java/backupmanager/gui/themes/ThemesManager.java @@ -0,0 +1,57 @@ +package backupmanager.gui.themes; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.formdev.flatlaf.FlatDarculaLaf; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatIntelliJLaf; +import com.formdev.flatlaf.FlatLightLaf; +import com.formdev.flatlaf.json.Json; +import com.formdev.flatlaf.themes.FlatMacDarkLaf; +import com.formdev.flatlaf.themes.FlatMacLightLaf; +import com.formdev.flatlaf.util.LoggingFacade; + +public class ThemesManager { + final List<ThemesInfo> bundledThemes = new ArrayList<>(); + final List<ThemesInfo> coreThemes = new ArrayList<>(); + + void loadThemes() { + bundledThemes.clear(); + // create core themes + + coreThemes.add(new ThemesInfo("FlatLaf Light", null, false, null, null, null, null, FlatLightLaf.class.getName())); + coreThemes.add(new ThemesInfo("FlatLaf Dark", null, true, null, null, null, null, FlatDarkLaf.class.getName())); + coreThemes.add(new ThemesInfo("FlatLaf IntelliJ", null, false, null, null, null, null, FlatIntelliJLaf.class.getName())); + coreThemes.add(new ThemesInfo("FlatLaf Darcula", null, true, null, null, null, null, FlatDarculaLaf.class.getName())); + coreThemes.add(new ThemesInfo("FlatLaf macOS Light", null, false, null, null, null, null, FlatMacLightLaf.class.getName())); + coreThemes.add(new ThemesInfo("FlatLaf macOS Dark", null, true, null, null, null, null, FlatMacDarkLaf.class.getName())); + + // load themes.json + Map<String, Object> json; + try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/themes/themes.json"), StandardCharsets.UTF_8)) { + json = (Map<String, Object>) Json.parse(reader); + } catch (IOException e) { + LoggingFacade.INSTANCE.logSevere(null, e); + return; + } + + // add themes info + for (Map.Entry<String, Object> e : json.entrySet()) { + String resourceName = e.getKey(); + Map<String, String> value = (Map<String, String>) e.getValue(); + String name = value.get("name"); + boolean dark = Boolean.parseBoolean(value.get("dark")); + String license = value.get("license"); + String licenseFile = value.get("licenseFile"); + String sourceCodeUrl = value.get("sourceCodeUrl"); + String sourceCodePath = value.get("sourceCodePath"); + bundledThemes.add(new ThemesInfo(name, resourceName, dark, license, licenseFile, sourceCodeUrl, sourceCodePath, null)); + } + } +} diff --git a/src/main/java/backupmanager/interfaces/ITranslatable.java b/src/main/java/backupmanager/interfaces/ITranslatable.java new file mode 100644 index 00000000..1fb6f21b --- /dev/null +++ b/src/main/java/backupmanager/interfaces/ITranslatable.java @@ -0,0 +1,5 @@ +package backupmanager.interfaces; + +public interface ITranslatable { + public void setTranslations(); +} diff --git a/src/main/resources/db/002_seed.sql b/src/main/resources/db/002_seed.sql index 6833c3e3..8541b030 100644 --- a/src/main/resources/db/002_seed.sql +++ b/src/main/resources/db/002_seed.sql @@ -1,6 +1,4 @@ INSERT OR IGNORE INTO Configurations (Code, Value) VALUES -('Language', 'eng.json'), -('Theme', 'light'), ('SubscriptionNedded', 'False'); INSERT OR IGNORE INTO SchemaVersion VALUES (1); diff --git a/src/main/resources/drawer/icon/about.svg b/src/main/resources/drawer/icon/about.svg new file mode 100644 index 00000000..9c07e834 --- /dev/null +++ b/src/main/resources/drawer/icon/about.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"> + <path d="M24 4C12.972292 4 4 12.972292 4 24C4 27.275316 4.8627078 30.334853 6.2617188 33.064453L4.09375 40.828125C3.5887973 42.631528 5.3719261 44.41261 7.1757812 43.908203L14.943359 41.740234C17.671046 43.137358 20.726959 44 24 44C35.027708 44 44 35.027708 44 24C44 12.972292 35.027708 4 24 4 z M 24 7C33.406292 7 41 14.593708 41 24C41 33.406292 33.406292 41 24 41C20.997029 41 18.192258 40.218281 15.744141 38.853516 A 1.50015 1.50015 0 0 0 14.609375 38.71875L7.2226562 40.78125L9.2851562 33.398438 A 1.50015 1.50015 0 0 0 9.1503906 32.263672C7.7836522 29.813476 7 27.004518 7 24C7 14.593708 14.593708 7 24 7 z M 23.976562 12.978516 A 1.50015 1.50015 0 0 0 22.5 14.5L22.5 26.5 A 1.50015 1.50015 0 1 0 25.5 26.5L25.5 14.5 A 1.50015 1.50015 0 0 0 23.976562 12.978516 z M 24 31 A 2 2 0 0 0 24 35 A 2 2 0 0 0 24 31 z" fill="#969696" /> +</svg> \ No newline at end of file diff --git a/src/main/resources/drawer/icon/chart.svg b/src/main/resources/drawer/icon/chart.svg new file mode 100644 index 00000000..f979f801 --- /dev/null +++ b/src/main/resources/drawer/icon/chart.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"> + <path d="M34.417969 1 A 1.50015 1.50015 0 0 0 32.951172 2.0097656L30.90625 7.9335938C28.361228 6.7082773 25.514539 6 22.5 6C11.748317 6 3 14.748317 3 25.5C3 36.251683 11.748317 45 22.5 45C33.251683 45 42 36.251683 42 25.5C42 22.486387 41.292976 19.640132 40.068359 17.095703L45.990234 15.050781 A 1.50015 1.50015 0 0 0 46.917969 13.142578C44.96816 7.4914915 40.507522 3.0318739 34.857422 1.0820312 A 1.50015 1.50015 0 0 0 34.417969 1 z M 35.242188 4.5703125C38.885137 6.2199623 41.780245 9.116043 43.429688 12.759766L30.923828 17.076172L35.242188 4.5703125 z M 21 9.0761719L21 24L6.0761719 24C6.7879478 16.072054 13.072054 9.7879478 21 9.0761719 z M 24 9.078125C26.115779 9.2721916 28.110401 9.8645323 29.921875 10.785156L27.082031 19.009766 A 1.50015 1.50015 0 0 0 28.990234 20.917969L37.214844 18.080078C38.350134 20.314523 39 22.823879 39 25.5C39 29.523922 37.56634 33.202685 35.183594 36.0625L24 24.878906L24 9.078125 z M 6.0761719 27L21.878906 27L33.0625 38.183594C30.202685 40.56634 26.523922 42 22.5 42C13.876334 42 6.8323757 35.422796 6.0761719 27 z" fill="#969696" /> +</svg> \ No newline at end of file diff --git a/src/main/resources/drawer/icon/dashboard.svg b/src/main/resources/drawer/icon/dashboard.svg new file mode 100644 index 00000000..3f8f7503 --- /dev/null +++ b/src/main/resources/drawer/icon/dashboard.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"> + <path d="M24 4C12.972066 4 4 12.972074 4 24C4 35.027926 12.972066 44 24 44C35.027934 44 44 35.027926 44 24C44 12.972074 35.027934 4 24 4 z M 24 7C33.406615 7 41 14.593391 41 24C41 33.406609 33.406615 41 24 41C14.593385 41 7 33.406609 7 24C7 14.593391 14.593385 7 24 7 z M 24 8.5 A 1.5 1.5 0 0 0 24 11.5 A 1.5 1.5 0 0 0 24 8.5 z M 14.101562 12.601562 A 1.5 1.5 0 0 0 14.101562 15.601562 A 1.5 1.5 0 0 0 14.101562 12.601562 z M 33.898438 12.601562 A 1.5 1.5 0 0 0 33.898438 15.601562 A 1.5 1.5 0 0 0 33.898438 12.601562 z M 34.498047 19.216797C32.208377 19.17827 24.126875 21.782219 23.234375 22.152344C22.214375 22.575344 21.729344 23.745625 22.152344 24.765625C22.575344 25.785625 23.745625 26.270656 24.765625 25.847656C25.786625 25.424656 35.508938 20.427203 35.085938 19.408203C35.033062 19.280578 34.825143 19.222301 34.498047 19.216797 z M 10 22.5 A 1.5 1.5 0 0 0 10 25.5 A 1.5 1.5 0 0 0 10 22.5 z M 38 22.5 A 1.5 1.5 0 0 0 38 25.5 A 1.5 1.5 0 0 0 38 22.5 z M 14.101562 32.398438 A 1.5 1.5 0 0 0 14.101562 35.398438 A 1.5 1.5 0 0 0 14.101562 32.398438 z M 33.898438 32.398438 A 1.5 1.5 0 0 0 33.898438 35.398438 A 1.5 1.5 0 0 0 33.898438 32.398438 z" fill="#969696" /> +</svg> \ No newline at end of file diff --git a/src/main/resources/drawer/icon/donate.svg b/src/main/resources/drawer/icon/donate.svg new file mode 100644 index 00000000..ef9b1f12 --- /dev/null +++ b/src/main/resources/drawer/icon/donate.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<svg fill="#969696" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4 21h9.62a3.995 3.995 0 0 0 3.037-1.397l5.102-5.952a1 1 0 0 0-.442-1.6l-1.968-.656a3.043 3.043 0 0 0-2.823.503l-3.185 2.547-.617-1.235A3.98 3.98 0 0 0 9.146 11H4c-1.103 0-2 .897-2 2v6c0 1.103.897 2 2 2zm0-8h5.146c.763 0 1.448.423 1.789 1.105l.447.895H7v2h6.014a.996.996 0 0 0 .442-.11l.003-.001.004-.002h.003l.002-.001h.004l.001-.001c.009.003.003-.001.003-.001.01 0 .002-.001.002-.001h.001l.002-.001.003-.001.002-.001.002-.001.003-.001.002-.001c.003 0 .001-.001.002-.001l.003-.002.002-.001.002-.001.003-.001.002-.001h.001l.002-.001h.001l.002-.001.002-.001c.009-.001.003-.001.003-.001l.002-.001a.915.915 0 0 0 .11-.078l4.146-3.317c.262-.208.623-.273.94-.167l.557.186-4.133 4.823a2.029 2.029 0 0 1-1.52.688H4v-6zM16 2h-.017c-.163.002-1.006.039-1.983.705-.951-.648-1.774-.7-1.968-.704L12.002 2h-.004c-.801 0-1.555.313-2.119.878C9.313 3.445 9 4.198 9 5s.313 1.555.861 2.104l3.414 3.586a1.006 1.006 0 0 0 1.45-.001l3.396-3.568C18.688 6.555 19 5.802 19 5s-.313-1.555-.878-2.121A2.978 2.978 0 0 0 16.002 2H16zm1 3c0 .267-.104.518-.311.725L14 8.55l-2.707-2.843C11.104 5.518 11 5.267 11 5s.104-.518.294-.708A.977.977 0 0 1 11.979 4c.025.001.502.032 1.067.485.081.065.163.139.247.222l.707.707.707-.707c.084-.083.166-.157.247-.222.529-.425.976-.478 1.052-.484a.987.987 0 0 1 .701.292c.189.189.293.44.293.707z"/></svg> diff --git a/src/main/resources/drawer/icon/forms.svg b/src/main/resources/drawer/icon/forms.svg new file mode 100644 index 00000000..96bf5279 --- /dev/null +++ b/src/main/resources/drawer/icon/forms.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"> + <path d="M12.5 4C10.032499 4 8 6.0324991 8 8.5L8 39.5C8 41.967501 10.032499 44 12.5 44L35.5 44C37.967501 44 40 41.967501 40 39.5L40 16.5 A 1.50015 1.50015 0 0 0 39.560547 15.439453L39.544922 15.423828L28.560547 4.4394531 A 1.50015 1.50015 0 0 0 27.5 4L12.5 4 z M 12.5 7L26 7L26 13.5C26 15.967501 28.032499 18 30.5 18L37 18L37 39.5C37 40.346499 36.346499 41 35.5 41L12.5 41C11.653501 41 11 40.346499 11 39.5L11 8.5C11 7.6535009 11.653501 7 12.5 7 z M 29 9.1210938L34.878906 15L30.5 15C29.653501 15 29 14.346499 29 13.5L29 9.1210938 z M 17.5 22 A 1.50015 1.50015 0 1 0 17.5 25L30.5 25 A 1.50015 1.50015 0 1 0 30.5 22L17.5 22 z M 17.5 28 A 1.50015 1.50015 0 1 0 17.5 31L30.5 31 A 1.50015 1.50015 0 1 0 30.5 28L17.5 28 z M 17.5 34 A 1.50015 1.50015 0 1 0 17.5 37L26.5 37 A 1.50015 1.50015 0 1 0 26.5 34L17.5 34 z" fill="#969696" /> +</svg> \ No newline at end of file diff --git a/src/main/resources/drawer/icon/github.svg b/src/main/resources/drawer/icon/github.svg new file mode 100644 index 00000000..8c129995 --- /dev/null +++ b/src/main/resources/drawer/icon/github.svg @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<svg width="800px" height="800px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>github [#142] + Created with Sketch. + + + + + + + + + + + + + diff --git a/src/main/resources/drawer/icon/help.svg b/src/main/resources/drawer/icon/help.svg new file mode 100644 index 00000000..24d75977 --- /dev/null +++ b/src/main/resources/drawer/icon/help.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/drawer/icon/history.svg b/src/main/resources/drawer/icon/history.svg new file mode 100644 index 00000000..0ff0f1d9 --- /dev/null +++ b/src/main/resources/drawer/icon/history.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/drawer/icon/info.svg b/src/main/resources/drawer/icon/info.svg new file mode 100644 index 00000000..724e1c6d --- /dev/null +++ b/src/main/resources/drawer/icon/info.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/drawer/icon/setting.svg b/src/main/resources/drawer/icon/setting.svg new file mode 100644 index 00000000..37d053fc --- /dev/null +++ b/src/main/resources/drawer/icon/setting.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/drawer/icon/subscription.svg b/src/main/resources/drawer/icon/subscription.svg new file mode 100644 index 00000000..2568017e --- /dev/null +++ b/src/main/resources/drawer/icon/subscription.svg @@ -0,0 +1,2 @@ + + diff --git a/src/main/resources/drawer/logo.svg b/src/main/resources/drawer/logo.svg new file mode 100644 index 00000000..c8dbafc2 --- /dev/null +++ b/src/main/resources/drawer/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/icons/add.svg b/src/main/resources/icons/add.svg new file mode 100644 index 00000000..351ea874 --- /dev/null +++ b/src/main/resources/icons/add.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/clear.svg b/src/main/resources/icons/clear.svg new file mode 100644 index 00000000..caeaa441 --- /dev/null +++ b/src/main/resources/icons/clear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/close.svg b/src/main/resources/icons/close.svg new file mode 100644 index 00000000..caeaa441 --- /dev/null +++ b/src/main/resources/icons/close.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/color.svg b/src/main/resources/icons/color.svg new file mode 100644 index 00000000..56ddfd9c --- /dev/null +++ b/src/main/resources/icons/color.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/copy.svg b/src/main/resources/icons/copy.svg new file mode 100644 index 00000000..1f6917f7 --- /dev/null +++ b/src/main/resources/icons/copy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/dashboard/customer.svg b/src/main/resources/icons/dashboard/customer.svg new file mode 100644 index 00000000..5cd7cbfb --- /dev/null +++ b/src/main/resources/icons/dashboard/customer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/dashboard/database.svg b/src/main/resources/icons/dashboard/database.svg new file mode 100644 index 00000000..43459820 --- /dev/null +++ b/src/main/resources/icons/dashboard/database.svg @@ -0,0 +1,19 @@ + + + + + database_system [#1797] + Created with Sketch. + + + + + + + + + + + + + diff --git a/src/main/resources/icons/dashboard/duration.svg b/src/main/resources/icons/dashboard/duration.svg new file mode 100644 index 00000000..cae6d1ba --- /dev/null +++ b/src/main/resources/icons/dashboard/duration.svg @@ -0,0 +1,2 @@ + + diff --git a/src/main/resources/icons/dashboard/expense.svg b/src/main/resources/icons/dashboard/expense.svg new file mode 100644 index 00000000..fddd2249 --- /dev/null +++ b/src/main/resources/icons/dashboard/expense.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/dashboard/income.svg b/src/main/resources/icons/dashboard/income.svg new file mode 100644 index 00000000..12d0c9a9 --- /dev/null +++ b/src/main/resources/icons/dashboard/income.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/dashboard/profit.svg b/src/main/resources/icons/dashboard/profit.svg new file mode 100644 index 00000000..10a8acd8 --- /dev/null +++ b/src/main/resources/icons/dashboard/profit.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/dashboard/rate.svg b/src/main/resources/icons/dashboard/rate.svg new file mode 100644 index 00000000..5edbb473 --- /dev/null +++ b/src/main/resources/icons/dashboard/rate.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/icons/dashboard/run.svg b/src/main/resources/icons/dashboard/run.svg new file mode 100644 index 00000000..88674750 --- /dev/null +++ b/src/main/resources/icons/dashboard/run.svg @@ -0,0 +1,14 @@ + + + + + Fill 1 + Created with Sketch. + + + + + + + + diff --git a/src/main/resources/icons/dashboard/usage.svg b/src/main/resources/icons/dashboard/usage.svg new file mode 100644 index 00000000..117888ec --- /dev/null +++ b/src/main/resources/icons/dashboard/usage.svg @@ -0,0 +1,2 @@ + + diff --git a/src/main/resources/icons/delete.svg b/src/main/resources/icons/delete.svg new file mode 100644 index 00000000..f7e63e6a --- /dev/null +++ b/src/main/resources/icons/delete.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/icons/donate.svg b/src/main/resources/icons/donate.svg new file mode 100644 index 00000000..befb59c9 --- /dev/null +++ b/src/main/resources/icons/donate.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/icons/edit.svg b/src/main/resources/icons/edit.svg new file mode 100644 index 00000000..2fddad3a --- /dev/null +++ b/src/main/resources/icons/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/icons/eye.svg b/src/main/resources/icons/eye.svg new file mode 100644 index 00000000..7f1214fc --- /dev/null +++ b/src/main/resources/icons/eye.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/icons/favorite.svg b/src/main/resources/icons/favorite.svg new file mode 100644 index 00000000..c640c7dd --- /dev/null +++ b/src/main/resources/icons/favorite.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/favorite_filled.svg b/src/main/resources/icons/favorite_filled.svg new file mode 100644 index 00000000..cd65f7a7 --- /dev/null +++ b/src/main/resources/icons/favorite_filled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/folder.svg b/src/main/resources/icons/folder.svg new file mode 100644 index 00000000..ec1299ff --- /dev/null +++ b/src/main/resources/icons/folder.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/icons/history.svg b/src/main/resources/icons/history.svg new file mode 100644 index 00000000..c14c7995 --- /dev/null +++ b/src/main/resources/icons/history.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/icons/info.svg b/src/main/resources/icons/info.svg new file mode 100644 index 00000000..b3cfdf0e --- /dev/null +++ b/src/main/resources/icons/info.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/icons/menu.svg b/src/main/resources/icons/menu.svg new file mode 100644 index 00000000..634f86bc --- /dev/null +++ b/src/main/resources/icons/menu.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/redo.svg b/src/main/resources/icons/redo.svg new file mode 100644 index 00000000..a45ace89 --- /dev/null +++ b/src/main/resources/icons/redo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/refresh.svg b/src/main/resources/icons/refresh.svg new file mode 100644 index 00000000..68d22ff3 --- /dev/null +++ b/src/main/resources/icons/refresh.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/search.svg b/src/main/resources/icons/search.svg new file mode 100644 index 00000000..2ddef201 --- /dev/null +++ b/src/main/resources/icons/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/support.svg b/src/main/resources/icons/support.svg new file mode 100644 index 00000000..5149f587 --- /dev/null +++ b/src/main/resources/icons/support.svg @@ -0,0 +1,6 @@ + + + support + + + \ No newline at end of file diff --git a/src/main/resources/icons/timer.svg b/src/main/resources/icons/timer.svg new file mode 100644 index 00000000..b9e4e5e0 --- /dev/null +++ b/src/main/resources/icons/timer.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/icons/undo.svg b/src/main/resources/icons/undo.svg new file mode 100644 index 00000000..5c3f351b --- /dev/null +++ b/src/main/resources/icons/undo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 7ea956ad..f4e0e4b8 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -10,12 +10,12 @@ - - + + - + @@ -38,11 +38,6 @@ - - - - - ${LOG_DIR}/error.log @@ -127,9 +122,9 @@ - + - + diff --git a/src/main/resources/res/config/config.json b/src/main/resources/res/config/config.json index 13fec9ac..60c69c57 100644 --- a/src/main/resources/res/config/config.json +++ b/src/main/resources/res/config/config.json @@ -12,28 +12,28 @@ "EMAIL": "assistenza@shardpc.it", "SHARD_WEBSITE": "https://www.shardpc.it/", "LOGO_IMG": "/res/img/logo.png", - "VERSION": "2.2.1", - "GUI_WIDTH": "982", - "GUI_HEIGHT": "715", + "VERSION": "3.0.0", + "GUI_WIDTH": "1366", + "GUI_HEIGHT": "768", + "GUI_MIN_WIDTH": "800", + "GUI_MIN_HEIGHT": "600", "MenuItems": { "BugReport": true, - "Preferences": true, - "Clear": false, + "Settings": true, "Donate": true, "PaypalDonate": true, "BuymeacoffeeDonate": true, "History": true, "InfoPage": true, - "New": true, - "Quit": true, - "Save": false, "Import": false, - "Export": false, - "SaveWithName": false, - "Share": true, + "Export": true, "Support": true, - "Website": true + "ContactUs": true, + "Website": true, + "BackupList": true, + "Dashboard": true, + "About": true }, "BackupService": { "value": 1, diff --git a/src/main/resources/res/img/logo.ico b/src/main/resources/res/img/logo.ico index 2d48e9d7..9a830e9c 100644 Binary files a/src/main/resources/res/img/logo.ico and b/src/main/resources/res/img/logo.ico differ diff --git a/src/main/resources/res/img/logo.png b/src/main/resources/res/img/logo.png index 830c0f66..9ae1fbcb 100644 Binary files a/src/main/resources/res/img/logo.png and b/src/main/resources/res/img/logo.png differ diff --git a/src/main/resources/res/img/logo_old.ico b/src/main/resources/res/img/logo_old.ico index ab96f69c..2d48e9d7 100644 Binary files a/src/main/resources/res/img/logo_old.ico and b/src/main/resources/res/img/logo_old.ico differ diff --git a/src/main/resources/res/img/logo_old.png b/src/main/resources/res/img/logo_old.png index 6e543187..830c0f66 100644 Binary files a/src/main/resources/res/img/logo_old.png and b/src/main/resources/res/img/logo_old.png differ diff --git a/src/main/resources/res/languages/deu.json b/src/main/resources/res/languages/deu.json index 5c8ca20b..07534e18 100644 --- a/src/main/resources/res/languages/deu.json +++ b/src/main/resources/res/languages/deu.json @@ -1,208 +1,267 @@ { - "General": { - "AppName": "Backup Manager", - "Backup": "Backup", - "Version": "Version", - "From": "Von", - "To": "Nach", - "OkButton": "Ok", - "CancelButton": "Abbrechen", - "CloseButton": "Schließen", - "ApplyButton": "Anwenden", - "SaveButton": "Speichern", - "CreateButton": "Erstellen" - }, - "Menu": { - "File": "Datei", - "Options": "Optionen", - "About": "Über", - "Help": "Hilfe", - "BugReport": "Fehler melden", - "Clear": "Löschen", - "Donate": "Spenden", - "History": "Verlauf", - "InfoPage": "Info", - "New": "Neu", - "Quit": "Beenden", - "Save": "Speichern", - "SaveWithName": "Speichern unter", - "Preferences": "Einstellungen", - "Import": "Backup-Liste importieren", - "Export": "Backup-Liste exportieren", - "Share": "Teilen", - "Support": "Support", - "Website": "Webseite" - }, - "TabbedFrames": { - "BackupEntry": "Backup-Eintrag", - "BackupList": "Backup-Liste" - }, - "BackupEntry": { - "PageTitle": "Backup-Eintrag", - "CurrentFile": "Aktuelle Datei", - "Notes": "Notizen", - "LastBackup": "Letztes Backup", - "SingleBackupButton": "Einzel-Backup", - "AutoBackupButton": "Auto-Backup", - "AutoBackupButtonON": "Auto-Backup (AN)", - "AutoBackupButtonOFF": "Auto-Backup (AUS)", - "InitialPathPlaceholder": "Anfangspfad", - "DestinationPathPlaceholder": "Zielpfad", - "BackupName": "Sicherungsname", - "BackupNameTooltip": "(Erforderlich) Sicherungsname", - "InitialPathTooltip": "(Erforderlich) Anfangspfad", - "DestinationPathTooltip": "(Erforderlich) Zielpfad", - "InitialFileChooserTooltip": "Dateiexplorer öffnen", - "DestinationFileChooserTooltip": "Dateiexplorer öffnen", - "NotesTooltip": "(Optional) Backup-Beschreibung", - "SingleBackupTooltip": "Backup durchführen", - "AutoBackupTooltip": "Automatisches Backup aktivieren/deaktivieren", - "TimePickerTooltip": "Zeitwähler", - "MaxBackupsToKeep": "Maximale Anzahl an Sicherungen beibehalten", - "MaxBackupsToKeepTooltip": "Maximale Anzahl an Sicherungen, bevor die ältesten entfernt werden." - }, - "BackupList": { - "BackupNameColumn": "Backup-Name", - "InitialPathColumn": "Anfangspfad", - "DestinationPathColumn": "Zielpfad", - "LastBackupColumn": "Letztes Backup", - "AutomaticBackupColumn": "Automatisches Backup", - "NextBackupDateColumn": "Nächstes Backup-Datum", - "TimeIntervalColumn": "Zeitintervall", - "BackupNameDetail": "Backup-Name", - "InitialPathDetail": "Anfangspfad", - "DestinationPathDetail": "Zielpfad", - "LastBackupDetail": "LetztesBackup", - "NextBackupDateDetail": "NächstesBackup", - "TimeIntervalDetail": "Zeitintervall", - "CreationDateDetail": "Erstellungsdatum", - "LastUpdateDateDetail": "LetzteAktualisierung", - "BackupCountDetail": "Backup-Anzahl", - "NotesDetail": "Notizen", - "MaxBackupsToKeepDetail": "MaximaleSicherungenBehalten", - "AddBackupTooltip": "Neues Backup hinzufügen", - "ExportAs": "Exportieren als: ", - "ExportAsPdfTooltip": "Exportieren als PDF", - "ExportAsCsvTooltip": "Exportieren als CSV", - "ResearchBarTooltip": "Suchleiste", - "ResearchBarPlaceholder": "Suchen...", - "EditPopup": "Bearbeiten", - "DeletePopup": "Löschen", - "InterruptPopup": "Unterbrechen", - "DuplicatePopup": "Duplizieren", - "RenameBackupPopup": "Backup umbenennen", - "OpenInitialFolderPopup": "Anfangspfad öffnen", - "OpenDestinationFolderPopup": "Zielpfad öffnen", - "BackupPopup": "Backup", - "SingleBackupPopup": "Einzel-Backup ausführen", - "AutoBackupPopup": "Auto-Backup", - "CopyTextPopup": "Text kopieren", - "CopyBackupNamePopup": "Backup-Name kopieren", - "CopyInitialPathPopup": "Anfangspfad kopieren", - "CopyDestinationPathPopup": "Zielpfad kopieren" - }, - "TimePickerDialog": { - "TimeIntervalTitle": "Zeitintervall für Auto-Backup", - "Description": "Wählen Sie, wie oft das automatische Backup durchgeführt werden soll, \nindem Sie die Frequenz in Tagen, Stunden und Minuten festlegen.", - "Days": "Tage", - "Hours": "Stunden", - "Minutes": "Minuten", - "SpinnerTooltip": "Mausrad zum Anpassen des Wertes" - }, - "PreferencesDialog": { - "PreferencesTitle": "Einstellungen", - "Language": "Sprache", - "Theme": "Thema" - }, - "UserDialog": { - "UserTitle": "Geben Sie Ihre Daten ein", - "Name": "Vorname", - "Surname": "Nachname", - "Email": "E-Mail", - "ErrorMessageForMissingData": "Bitte füllen Sie alle erforderlichen Felder aus.", - "ErrorMessageForWrongEmail": "Die angegebene E-Mail-Adresse ist ungültig. Bitte geben Sie eine korrekte Adresse an.", - "EmailConfirmationSubject": "Vielen Dank, dass Sie sich für Backup Manager entschieden haben!", - "EmailConfirmationBody": "Hallo [UserName],\n\nVielen Dank, dass Sie Backup Manager heruntergeladen und registriert haben - Ihr neues Tool für eine sichere und effiziente Verwaltung Ihrer Backups!\n\nDies ist eine automatisierte E-Mail, die zur Bestätigung Ihrer Registrierung gesendet wurde. Wir werden Sie nur per E-Mail kontaktieren, um Sie über neue Versionen oder wichtige Updates der Anwendung zu informieren.\n\nFalls Sie Fragen haben, Unterstützung benötigen oder Vorschläge machen möchten, stehen wir Ihnen jederzeit gerne zur Verfügung. Sie können uns unter [SupportEmail] erreichen.\n\nVielen Dank nochmals, dass Sie sich für Backup Manager entschieden haben, und viel Erfolg bei der Verwaltung Ihrer Backups!\n\nMit freundlichen Grüßen,\nDas Backup Manager-Team" - }, - "ProgressBackupFrame": { - "ProgressBackupTitle": "Backup läuft", - "StatusCompleted": "Backup abgeschlossen!", - "StatusLoading": "Lädt..." - }, - "TrayIcon": { - "TrayTooltip": "Backup-Dienst", - "OpenAction": "Schnellzugriff", - "ExitAction": "Beenden", - "SuccessMessage": "\nDas Backup wurde erfolgreich abgeschlossen:", - "ErrorMessageInputMissing": "\nFehler beim automatischen Backup.\nEingabe fehlt!", - "ErrorMessageFilesNotExisting": "\nFehler beim automatischen Backup.\nEin oder beide Pfade existieren nicht!", - "ErrorMessageSamePaths": "\nFehler beim automatischen Backup.\nDer Anfangspfad und der Zielpfad dürfen nicht gleich sein. Bitte wählen Sie unterschiedliche Pfade!" - }, - "Dialogs": { - "ErrorGenericTitle": "Fehler", - "WarningGenericTitle": "Warnung", - "WarningBackupAlreadyInProgressMessage": "Es läuft bereits eine Sicherung. Es ist nicht möglich, parallele Sicherungen durchzuführen.", - "WarningShortTimeIntervalMessage": "Das ausgewählte Zeitintervall ist sehr kurz. Für eine optimale Leistung empfehlen wir, es auf mindestens eine Stunde einzustellen. Möchten Sie dennoch fortfahren?", - "ErrorMessageForFolderNotExisting": "Der Ordner existiert nicht oder ist ungültig", - "ErrorMessageForSavingFileWithPathsEmpty": "Die Datei konnte nicht gespeichert werden. Sowohl der Anfangs- als auch der Zielpfad müssen angegeben werden und dürfen nicht leer sein", - "BackupSavedCorrectlyTitle": "Backup gespeichert", - "BackupSavedCorrectlyMessage": "erfolgreich gespeichert!", - "ErrorSavingBackupMessage": "Fehler beim Speichern des Backups", - "BackupNameInput": "Name des Backups", - "ConfirmationRequiredTitle": "Bestätigung erforderlich", - "DuplicatedBackupNameMessage": "Ein Backup mit demselben Namen existiert bereits. Möchten Sie es überschreiben?", - "BackupNameAlreadyUsedMessage": "Backup-Name bereits verwendet!", - "ErrorMessageForIncorrectInitialPath": "Fehler beim Backup-Vorgang: Der Anfangspfad ist falsch!", - "ExceptionMessageTitle": "Fehler...", - "ExceptionMessageClipboardMessage": "Fehlertext wurde in die Zwischenablage kopiert.", - "ExceptionMessageClipboardButton": "In Zwischenablage kopieren", - "ExceptionMessageReportButton": "Problem melden", - "ExceptionMessageReportMessage": "Bitte melden Sie diesen Fehler, entweder mit einem Screenshot oder indem Sie den folgenden Fehlertext kopieren (es ist hilfreich, eine Beschreibung der durchgeführten Aktionen vor dem Fehler anzugeben):", - "ErrorMessageOpeningWebsite": "Die Webseite konnte nicht geöffnet werden. Bitte versuchen Sie es erneut.", - "ConfirmationMessageForClear": "Sind Sie sicher, dass Sie die Felder bereinigen möchten?", - "ConfirmationMessageForUnsavedChanges": "Es gibt nicht gespeicherte Änderungen. Möchten Sie sie speichern, bevor Sie zu einem anderen Backup wechseln?", - "ErrorMessageOpenHistoryFile": "Fehler beim Öffnen der Verlaufsdatei.", - "ConfirmationMessageBeforeDeleteBackup": "Sind Sie sicher, dass Sie dieses Element löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", - "ShareLinkCopiedMessage": "Freigabelink wurde in die Zwischenablage kopiert!", - "ConfirmationMessageCancelAutoBackup": "Sind Sie sicher, dass Sie automatische Backups für diesen Eintrag deaktivieren möchten?", - "ErrorMessageUnableToSendEmail": "E-Mail konnte nicht gesendet werden. Bitte versuchen Sie es später erneut.", - "ErrorMessageNotSupportedEmail": "Ihr System unterstützt das Senden von E-Mails direkt aus dieser Anwendung nicht.", - "ErrorMessageNotSupportedEmailGeneric": "Ihr System unterstützt das Senden von E-Mails nicht.", - "ErrorWrongTimeInterval": "Das Zeitintervall ist nicht korrekt", - "AutoBackupActivatedMessage": "Auto-Backup wurde aktiviert", - "SettedEveryMessage": "\nWird eingestellt auf alle", - "DaysMessage": " Tage", - "InterruptBackupProcessMessage": "Sind Sie sicher, dass Sie dieses Backup abbrechen möchten?", - "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!", - "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.", - "ErrorMessageCountingFiles": "Fehler beim Zählen der zu sichernden Dateien.", - "ErrorMessageZippingGeneric": "Fehler beim Komprimieren der Dateien.", - "ErrorMessageZippingIO": "Fehler beim Komprimieren der Dateien: E/A-Fehler.", - "ErrorMessageZippingSecurity": "Fehler beim Komprimieren der Dateien: Sicherheitsfehler.", - "SuccessGenericTitle": "Erfolg", - "SuccessfullyExportedToCsvMessage": "Backups erfolgreich als CSV exportiert!", - "SuccessfullyExportedToPdfMessage": "Backups erfolgreich als PDF exportiert!", - "ErrorMessageForExportingToCsv": "Fehler beim Exportieren der Backups in CSV: ", - "ErrorMessageForExportingToPdf": "Fehler beim Exportieren der Backups in PDF: ", - "CsvNameMessageInput": "Geben Sie den Namen der CSV-Datei ein.", - "PdfNameMessageInput": "Geben Sie den Namen der PDF-Datei ein.", - "DuplicatedFileNameMessage": "Datei existiert bereits. Überschreiben?", - "ErrorMessageInvalidFilename": "Ungültiger Dateiname. Verwenden Sie nur alphanumerische Zeichen, Bindestriche und Unterstriche.", - "ConfirmationDeletionTitle": "Löschen bestätigen", - "ConfirmationDeletionMessage": "Sind Sie sicher, dass Sie die ausgewählten Zeilen löschen möchten?" - }, - "Subscription": { - "ExpiringTitle": "Backup Manager Abonnement läuft bald ab", - "ExpiringMessage": "Ihr Backup Manager Abonnement läuft bald ab.\nAutomatische Backups werden bis zum Ablaufdatum weiterhin ausgeführt.\nBitte kontaktieren Sie den Support, um es zu verlängern.", - "ExpiredTitle": "Backup Manager Abonnement abgelaufen", - "ExpiredMessage": "Ihr Backup Manager Abonnement ist abgelaufen.\nAutomatische Backups werden nicht mehr ausgeführt.\nBitte kontaktieren Sie den Support, um es zu reaktivieren." - } -} \ No newline at end of file + "General": { + "AppName": "Backup Manager", + "CancelButton": "Abbrechen", + "Backup": "Backup", + "Version": "Version", + "ApplyButton": "Anwenden", + "OkButton": "Ok", + "From": "Von", + "SaveButton": "Speichern", + "CloseButton": "Schließen", + "CreateButton": "Erstellen", + "EditButton": "Bearbeiten", + "DeleteButton": "Löschen", + "QuickSearch": "Schnellsuche", + "ContactUs": "Kontaktieren Sie uns", + "To": "Nach" + }, + "TrayIcon": { + "SuccessMessage": "\nDas Backup wurde erfolgreich abgeschlossen:", + "ExitAction": "Beenden", + "ErrorMessageFilesNotExisting": "\nFehler beim automatischen Backup.\nEin oder beide Pfade existieren nicht!", + "TrayTooltip": "Backup-Dienst", + "OpenAction": "Schnellzugriff", + "ErrorMessageInputMissing": "\nFehler beim automatischen Backup.\nEingabe fehlt!", + "ErrorMessageSamePaths": "\nFehler beim automatischen Backup.\nDer Anfangspfad und der Zielpfad dürfen nicht gleich sein. Bitte wählen Sie unterschiedliche Pfade!" + }, + "Dialogs": { + "WarningGenericTitle": "Warnung", + "ErrorMessageForPathNotExisting": "Ein oder beide Pfade existieren nicht!", + "ExceptionMessageReportMessage": "Bitte melden Sie diesen Fehler, entweder mit einem Screenshot oder indem Sie den folgenden Fehlertext kopieren (es ist hilfreich, eine Beschreibung der durchgeführten Aktionen vor dem Fehler anzugeben):", + "ErrorGenericTitle": "Fehler", + "ErrorMessageForSamePaths": "Der Anfangspfad und der Zielpfad dürfen nicht gleich sein. Bitte wählen Sie unterschiedliche Pfade!", + "SettedEveryMessage": "\nWird eingestellt auf alle", + "WarningBackupAlreadyInProgressMessage": "Es läuft bereits eine Sicherung. Es ist nicht möglich, parallele Sicherungen durchzuführen.", + "WarningShortTimeIntervalMessage": "Das ausgewählte Zeitintervall ist sehr kurz. Für eine optimale Leistung empfehlen wir, es auf mindestens eine Stunde einzustellen. Möchten Sie dennoch fortfahren?", + "ConfirmationMessageCancelAutoBackup": "Sind Sie sicher, dass Sie automatische Backups für diesen Eintrag deaktivieren möchten?", + "ErrorMessageCountingFiles": "Fehler beim Zählen der zu sichernden Dateien.", + "ErrorMessageInputMissingGeneric": "Eingabe fehlt!", + "BackupNameInput": "Name des Backups", + "CsvNameMessageInput": "Geben Sie den Namen der CSV-Datei ein.", + "ErrorMessageZippingGeneric": "Fehler beim Komprimieren der Dateien.", + "ErrorMessageZippingIO": "Fehler beim Komprimieren der Dateien: E/A-Fehler.", + "DuplicatedFileNameMessage": "Datei existiert bereits. Überschreiben?", + "AutoBackupActivatedMessage": "Auto-Backup wurde aktiviert", + "AutoBackup": "Automatische Sicherung", + "DaysMessage": " Tage", + "ExceptionMessageReportButton": "Problem melden", + "SuccessGenericTitle": "Erfolg", + "DuplicatedBackupNameMessage": "Ein Backup mit demselben Namen existiert bereits. Möchten Sie es überschreiben?", + "ExceptionMessageClipboardButton": "In Zwischenablage kopieren", + "ErrorMessageZippingSecurity": "Fehler beim Komprimieren der Dateien: Sicherheitsfehler.", + "InterruptBackupProcessMessage": "Sind Sie sicher, dass Sie dieses Backup abbrechen möchten?", + "ConfirmationMessageBeforeDeleteBackup": "Sind Sie sicher, dass Sie dieses Element löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", + "ConfirmationRequiredTitle": "Bestätigung erforderlich" + }, + "TimePickerDialog": { + "TimeIntervalTitle": "Zeitintervall für Auto-Backup", + "Days": "Tage", + "Hours": "Stunden", + "SpinnerTooltip": "Mausrad zum Anpassen des Wertes", + "Description": "Wählen Sie, wie oft das automatische Backup durchgeführt werden soll, \nindem Sie die Frequenz in Tagen, Stunden und Minuten festlegen.", + "Minutes": "Minuten" + }, + "Subscription": { + "ExpiredTitle": "Backup Manager Abonnement abgelaufen", + "ExpiringMessage": "Ihr Backup Manager Abonnement läuft bald ab.\nAutomatische Backups werden bis zum Ablaufdatum weiterhin ausgeführt.\nBitte kontaktieren Sie den Support, um es zu verlängern.", + "ExpiringTitle": "Backup Manager Abonnement läuft bald ab", + "ExpiredMessage": "Ihr Backup Manager Abonnement ist abgelaufen.\nAutomatische Backups werden nicht mehr ausgeführt.\nBitte kontaktieren Sie den Support, um es zu reaktivieren.", + "ActiveLabel": "Aktiv", + "ExpiringLabel": "Läuft bald ab", + "ExpiredLabel": "Abgelaufen", + "Status": "Abonnementstatus", + "ValidFrom": "Gültig ab", + "ValidTo": "Gültig bis", + "ToExtend": "um den Abonnementzeitraum zu verlängern." + }, + "ProgressBackupFrame": { + "StatusLoading": "Lädt...", + "ProgressBackupTitle": "Backup läuft", + "StatusCompleted": "Backup abgeschlossen!" + }, + "Menu": { + "About": "Über", + "File": "Datei", + "BugReport": "Fehler melden", + "Options": "Optionen", + "New": "Neu", + "Help": "Hilfe", + "History": "Verlauf", + "SubmenuMain": "HAUPT", + "SubmenuOther": "ANDERE", + "Donate": "Projekt unterstützen", + "Backups": "Backup-Liste", + "ImportBackup": "Backups aus CSV importieren", + "ExportBackup": "Backups in CSV exportieren", + "Dashboard": "Dashboard", + "Github": "GitHub-Seite", + "Paypal": "PayPal", + "BuyMeACoffe": "Spendiere mir einen Kaffee", + "Subscription": "Abonnement" + }, + "BackupList": { + "NotesDetail": "Notizen", + "BackupNameDetail": "Backup-Name", + "EditPopup": "Bearbeiten", + "ResearchBarTooltip": "Suchleiste", + "InitialPathDetail": "Anfangspfad", + "RenameBackupPopup": "Backup umbenennen", + "NextBackupDateDetail": "NächstesBackup", + "AutoBackupPopup": "Auto-Backup", + "BackupNameColumn": "Backup-Name", + "BackupPopup": "Backup", + "BackupCountDetail": "Backup-Anzahl", + "InitialPathColumn": "Anfangspfad", + "MaxBackupsToKeepDetail": "MaximaleSicherungenBehalten", + "DeletePopup": "Löschen", + "OpenDestinationFolderPopup": "Zielpfad öffnen", + "LastBackupColumn": "Letztes Backup", + "SingleBackupPopup": "Einzel-Backup ausführen", + "CopyTextPopup": "Text kopieren", + "DestinationPathDetail": "Zielpfad", + "CopyDestinationPathPopup": "Zielpfad kopieren", + "LastBackupDetail": "LetztesBackup", + "OpenInitialFolderPopup": "Anfangspfad öffnen", + "ResearchBarPlaceholder": "Suchen...", + "InterruptPopup": "Unterbrechen", + "DuplicatePopup": "Duplizieren", + "NextBackupDateColumn": "Nächstes Backup-Datum", + "DestinationPathColumn": "Zielpfad", + "AutomaticBackupColumn": "Automatisches Backup", + "LastUpdateDateDetail": "LetzteAktualisierung", + "TimeIntervalDetail": "Zeitintervall", + "CopyBackupNamePopup": "Backup-Name kopieren", + "CreationDateDetail": "Erstellungsdatum", + "CopyInitialPathPopup": "Anfangspfad kopieren", + "BackupListTitle": "Backup-Liste", + "BackupListDescription": "Verwalten und überwachen Sie Backup-Konfigurationen, einschließlich Erstellung, Bearbeitung, Planung und Ausführung.", + "TimeIntervalColumn": "Intervall (tt.HH:mm)", + "MaxBackupsColumn": "Maximale Anzahl aufzubewahrender Backups" + }, + "UserDialog": { + "EmailConfirmationBody": "Hallo [UserName],\n\nVielen Dank, dass Sie Backup Manager heruntergeladen und registriert haben - Ihr neues Tool für eine sichere und effiziente Verwaltung Ihrer Backups!\n\nDies ist eine automatisierte E-Mail, die zur Bestätigung Ihrer Registrierung gesendet wurde. Wir werden Sie nur per E-Mail kontaktieren, um Sie über neue Versionen oder wichtige Updates der Anwendung zu informieren.\n\nFalls Sie Fragen haben, Unterstützung benötigen oder Vorschläge machen möchten, stehen wir Ihnen jederzeit gerne zur Verfügung. Sie können uns unter [SupportEmail] erreichen.\n\nVielen Dank nochmals, dass Sie sich für Backup Manager entschieden haben, und viel Erfolg bei der Verwaltung Ihrer Backups!\n\nMit freundlichen Grüßen,\nDas Backup Manager-Team", + "EmailConfirmationSubject": "Vielen Dank, dass Sie sich für Backup Manager entschieden haben!", + "Surname": "Nachname", + "Name": "Vorname", + "Email": "E-Mail", + "UserTitle": "Geben Sie Ihre Daten ein", + "UserDescription": "Bitte geben Sie Ihre Daten ein, um auf das System zuzugreifen", + "UserNamePlaceholder": "Geben Sie Ihren Namen ein", + "UserSurnamePlaceholder": "Geben Sie Ihren Nachnamen ein", + "UserEmailPlaceholder": "Geben Sie Ihre E-Mail-Adresse ein" + }, + "BackupEntry": { + "TimePickerTooltip": "Zeitwähler", + "MaxBackupsToKeepTooltip": "Maximale Anzahl an Sicherungen, bevor die ältesten entfernt werden.", + "BackupName": "Sicherungsname", + "InitialPathTooltip": "(Erforderlich) Anfangspfad", + "InitialFileChooserTooltip": "Dateiexplorer öffnen", + "LastBackup": "Letztes Backup", + "SingleBackupButton": "Einzel-Backup", + "AutoBackupButtonON": "Auto-Backup (AN)", + "AutoBackupButtonOFF": "Auto-Backup (AUS)", + "DestinationPathTooltip": "(Erforderlich) Zielpfad", + "MaxBackupsToKeep": "Maximale Anzahl an Sicherungen beibehalten", + "SingleBackupTooltip": "Backup durchführen", + "AutoBackupTooltip": "Automatisches Backup aktivieren/deaktivieren", + "NotesTooltip": "(Optional) Backup-Beschreibung", + "DestinationFileChooserTooltip": "Dateiexplorer öffnen", + "BackupNameTooltip": "(Erforderlich) Sicherungsname", + "Notes": "Notizen", + "PageSubtitleCreate": "Backup erstellen", + "PageSubtitleEdit": "Backup bearbeiten", + "PageSubtitleInfo": "Backup-Informationen", + "PageSubtitleSettings": "Backup-Einstellungen", + "Paths": "Pfade", + "BackupNamePlaceholder": "Backup-Name (eindeutig)", + "InitialPathPlaceholder": "Quellpfad z. B. C:\\Users\\Admin\\Documents", + "DestinationPathPlaceholder": "Zielordner z. B. D:\\Backups" + }, + "HistoryLogs": { + "HistoryLogsTitle": "Verlaufsprotokolle", + "HistoryLogsDescription": "Hier finden Sie die Anwendungsprotokolle, die bei der Fehlerbehebung helfen und das Verhalten der Anwendung im Laufe der Zeit verständlich machen." + }, + "About": { + "AboutSystemInformation": "Systeminformationen", + "AboutMessageBody": "Backup Manager ist eine einfache und leistungsstarke Anwendung zur Automatisierung von Sicherungen von Ordnern und Unterordnern.

Benutzer können automatische Backups planen oder jederzeit manuelle Backups ausführen.

Die Backup-Historie wird sicher gespeichert und ermöglicht die vollständige Kontrolle über gespeicherte Daten.

Besuchen Sie die Projektwebsite für weitere Informationen.

" + }, + "Dashboard": { + "DashboardTitle": "Backup-Analyse-Dashboard", + "DashboardCardTotalConfigurations": "Gesamte Backup-Konfigurationen", + "DashboardCardTotalExecutions": "Gesamte Backup-Ausführungen", + "DashboardCardSuccessRate": "Erfolgsrate", + "DashboardCardAvgDuration": "Durchschnittliche Backup-Dauer", + "DashboardCardCompressionRate": "Komprimierungsrate", + "DashboardChartExecutions": "Backup-Ausführungen", + "DashboardChartAvgDuration": "Durchschnittliche Backup-Dauer (Min.)" + }, + "Settings": { + "SettingsLayoutTab": "Layout", + "SettingsStyleTab": "Stil", + "SettingsWindowsLayout": "Fensterlayout", + "SettingsWindowsRight": "Von rechts nach links", + "SettingsWindowsFull": "Vollständiger Fensterinhalt", + "SettingsDrawerLayout": "Panel-Layout", + "SettingsDrawerLeft": "Links", + "SettingsDrawerLeading": "Anfang", + "SettingsDrawerTrailing": "Ende", + "SettingsDrawerRight": "Rechts", + "SettingsDrawerTop": "Oben", + "SettingsDrawerBottom": "Unten", + "SettingsModalOption": "Standard-Modaloption", + "SettingsModalAnimation": "Animation aktivieren", + "SettingsModalClose": "Mit ESC schließen", + "SettingsLanguagesLayout": "Sprache", + "SettingsAccentLayout": "Akzentfarbe", + "SettingsColorPickerLayout": "Farbauswahl", + "SettingsDrawerLineLayout": "Panel-Linienstil", + "SettingsDrawerLineCurved": "Gebogene Linie", + "SettingsDrawerDotLine": "Gepunktete gerade Linie", + "SettingsLineStyleLayout": "Linienstiloption", + "SettingsLineStyleRettangle": "Rechteck", + "SettingsLineStyleEllipse": "Ellipse", + "SettingsLineStyleLine": "Linie", + "SettingsLineStyleCurved": "Gebogen", + "SettingsColorOptionLayout": "Farboption", + "SettingsColorOptionPainted": "Ausgewählte Linie einfärben", + "SettingsThemes": "Designs", + "SettingsThemeAll": "Alle", + "SettingsThemeLight": "Hell", + "SettingsThemeDark": "Dunkel" + }, + "SearchBar": { + "SearcTitle": "Suchen...", + "SearchNoRecent": "Keine letzten Suchen", + "SearchNoResult": "Kein Ergebnis für", + "SearchFavorite": "Favorit", + "SearchRecent": "Letzte" + }, + "Toast": { + "BackupEditedOk": "Backup erfolgreich aktualisiert", + "BackupCreatedOk": "Backup erfolgreich erstellt", + "BackupDeletedOk": "Backup erfolgreich gelöscht", + "BackupReplacedOk": "Vorhandenes Backup erfolgreich ersetzt", + "BackupDeletedError": "Backup konnte nicht gelöscht werden", + "BackupReplacedError": "Backup konnte nicht ersetzt werden", + "InvalidTime": "Ungültiges Zeitintervall", + "SubscriptionExpiring": "Ihr Backup Manager-Abonnement läuft bald ab. Bitte kontaktieren Sie uns zur Verlängerung", + "SubscriptionExpired": "Ihr Backup Manager-Abonnement ist abgelaufen. Bitte kontaktieren Sie uns zur Verlängerung", + "LanguageChange": "Einige Änderungen werden nach einem Neustart der Anwendung wirksam", + "MissingDataLoginError": "Bitte füllen Sie alle erforderlichen Felder aus", + "WrongEmailLoginError": "Die angegebene E-Mail-Adresse ist ungültig. Bitte geben Sie eine korrekte ein", + "LoginOk": "Willkommen! Sie haben sich erfolgreich angemeldet", + "ErrorTextClipboard": "Der Fehlertext wurde in die Zwischenablage kopiert", + "CsvExport": "Backups erfolgreich als CSV exportiert!", + "CsvExportError": "Fehler beim Exportieren der Backups als CSV", + "CsvExportInvalidFilename": "Ungültiger Dateiname. Verwenden Sie nur alphanumerische Zeichen, Bindestriche und Unterstriche", + "NotSupportedEmail": "Ihr System unterstützt das direkte Senden von E-Mails aus dieser Anwendung nicht", + "UnableToSendEmail": "E-Mail konnte nicht gesendet werden. Bitte versuchen Sie es später erneut", + "NotSupportedEmailGeneric": "Ihr System unterstützt das Senden von E-Mails nicht", + "OpeningWebsiteError": "Webseite konnte nicht geöffnet werden. Bitte versuchen Sie es erneut", + "FolderNotExisting": "Der Ordner existiert nicht oder ist ungültig", + "BackupNameAlreadyUsed": "Backup-Name bereits verwendet!", + "BackupAlreadyInProgress": "Es läuft bereits ein Backup. Parallele Backups sind nicht möglich", + "BackupPathsEmptyError": "The initial path and destination path cannot be empty!" + } +} diff --git a/src/main/resources/res/languages/eng.json b/src/main/resources/res/languages/eng.json index 5b5e441b..b456d698 100644 --- a/src/main/resources/res/languages/eng.json +++ b/src/main/resources/res/languages/eng.json @@ -1,208 +1,267 @@ { - "General": { - "AppName": "Backup Manager", - "Backup": "Backup", - "Version": "Version", - "From": "From", - "To": "A", - "OkButton": "Ok", - "CancelButton": "Cancel", - "CloseButton":"Close", - "ApplyButton":"Apply", - "SaveButton": "Save", - "CreateButton": "Create" - }, - "Menu": { - "File": "File", - "Options": "Options", - "About": "About", - "Help": "Help", - "BugReport": "Report a bug", - "Clear": "Clear", - "Donate": "Donate", - "History": "History", - "InfoPage": "Info", - "New": "New", - "Quit": "Quit", - "Save": "Save", - "SaveWithName": "Save with name", - "Preferences": "Preferences", - "Import": "Import backup list", - "Export": "Export backup list", - "Share": "Share", - "Support": "Support", - "Website": "Website" - }, - "TabbedFrames": { - "BackupEntry": "BackupEntry", - "BackupList": "BackupList" - }, - "BackupEntry": { - "PageTitle": "Backup Entry", - "CurrentFile": "Current file", - "Notes": "Notes", - "LastBackup": "Last backup", - "SingleBackupButton": "Single Backup", - "AutoBackupButton": "Auto Backup", - "AutoBackupButtonON": "Auto Backup (ON)", - "AutoBackupButtonOFF": "Auto Backup (OFF)", - "InitialPathPlaceholder": "Initial path", - "DestinationPathPlaceholder": "Destination path", - "BackupName": "Backup name", - "BackupNameTooltip": "(Required) Backup name", - "InitialPathTooltip": "(Required) Initial path", - "DestinationPathTooltip": "(Required) Destination path", - "InitialFileChooserTooltip": "Open file explorer", - "DestinationFileChooserTooltip": "Open file explorer", - "NotesTooltip": "(Optional) Backup description", - "SingleBackupTooltip": "Perform the backup", - "AutoBackupTooltip": "Enable/Disable automatic backup", - "TimePickerTooltip": "Time picker", - "MaxBackupsToKeep": "Max backups to keep", - "MaxBackupsToKeepTooltip": "Maximum number of backups before removing the oldest." - }, - "BackupList": { - "BackupNameColumn": "Backup Name", - "InitialPathColumn": "Initial Path", - "DestinationPathColumn": "Destination Path", - "LastBackupColumn": "Last Backup", - "AutomaticBackupColumn": "Automatic Backup", - "NextBackupDateColumn": "Next Backup Date", - "TimeIntervalColumn": "Time Interval", - "BackupNameDetail": "BackupName", - "InitialPathDetail": "InitialPath", - "DestinationPathDetail": "DestinationPath", - "LastBackupDetail": "LastBackup", - "NextBackupDateDetail": "NextBackup", - "TimeIntervalDetail": "TimeInterval", - "CreationDateDetail": "CreationDate", - "LastUpdateDateDetail": "LastUpdateDate", - "BackupCountDetail": "BackupCount", - "NotesDetail": "Notes", - "MaxBackupsToKeepDetail": "MaxBackupsToKeep", - "AddBackupTooltip": "Add new backup", - "ExportAs": "Export as: ", - "ExportAsPdfTooltip": "Export as PDF", - "ExportAsCsvTooltip": "Export as CSV", - "ResearchBarTooltip": "Research bar", - "ResearchBarPlaceholder": "Search...", - "EditPopup": "Edit", - "DeletePopup": "Delete", - "InterruptPopup": "Interrupt", - "DuplicatePopup": "Duplicate", - "RenameBackupPopup": "Rename backup", - "OpenInitialFolderPopup": "Open initial path", - "OpenDestinationFolderPopup": "Open destination path", - "BackupPopup": "Backup", - "SingleBackupPopup": "Run single backup", - "AutoBackupPopup": "Auto backup", - "CopyTextPopup": "Copy text", - "CopyBackupNamePopup": "Copy backup name", - "CopyInitialPathPopup": "Copy initial path", - "CopyDestinationPathPopup": "Copy destination path" - }, - "TimePickerDialog": { - "TimeIntervalTitle": "Time interval for auto backup", - "Description": "Select how often to perform the automatic backup by \nchoosing the frequency in days, hours, and minutes.", - "Days": "Days", - "Hours": "Hours", - "Minutes": "Minutes", - "SpinnerTooltip": "Mouse wheel to adjust the value" - }, - "PreferencesDialog": { - "PreferencesTitle": "Preferences", - "Language": "Language", - "Theme": "Theme" - }, - "UserDialog": { - "UserTitle": "Insert your data", - "Name": "Name", - "Surname": "Surname", - "Email": "Email", - "ErrorMessageForMissingData": "Please fill in all the required fields.", - "ErrorMessageForWrongEmail": "The provided email address is invalid. Please provide a correct one.", - "EmailConfirmationSubject": "Thank you for choosing Backup Manager!", - "EmailConfirmationBody": "Hi [UserName],\n\nThank you for downloading and registering Backup Manager, your new tool for secure and efficient backup management!\n\nThis is an automated email sent to confirm your registration. We will contact you by email only to inform you about new releases or important updates of the application.\n\nIn the meantime, if you have any questions, need assistance, or have suggestions, we are always here for you. You can reach us at [SupportEmail].\n\nThank you again for choosing Backup Manager, and enjoy managing your backups!\n\nBest regards,\nThe Backup Manager Team" - }, - "ProgressBackupFrame": { - "ProgressBackupTitle":"Backup in progress", - "StatusCompleted":"Backup completed!", - "StatusLoading":"Loading..." - }, - "TrayIcon": { - "TrayTooltip":"Backup Service", - "OpenAction": "Quick Access", - "ExitAction": "Exit", - "SuccessMessage":"\nThe backup was successfully completed:", - "ErrorMessageInputMissing":"\nError during automatic backup.\nInput Missing!", - "ErrorMessageFilesNotExisting":"\nError during automatic backup.\nOne or both paths do not exist!", - "ErrorMessageSamePaths":"\nError during automatic backup.\nThe initial path and destination path cannot be the same. Please choose different paths!" - }, - "Dialogs": { - "ErrorGenericTitle":"Error", - "WarningGenericTitle": "Warning", - "WarningBackupAlreadyInProgressMessage": "There is already a backup in progress. It is not possible to perform parallel backups", - "WarningShortTimeIntervalMessage": "The selected time interval is very short. For optimal performance, we recommend setting it to at least one hour. Do you still want to proceed?", - "ErrorMessageForFolderNotExisting":"The folder does not exist or is invalid", - "ErrorMessageForSavingFileWithPathsEmpty":"Unable to save the file. Both the initial and destination paths must be specified and cannot be empty", - "BackupSavedCorrectlyTitle":"Backup saved", - "BackupSavedCorrectlyMessage":"saved successfully!", - "ErrorSavingBackupMessage":"Error saving backup", - "BackupNameInput":"Name of the backup", - "ConfirmationRequiredTitle":"Confirmation required", - "DuplicatedBackupNameMessage":"A backup with the same name already exists, do you want to overwrite it?", - "BackupNameAlreadyUsedMessage":"Backup name already used!", - "ErrorMessageForIncorrectInitialPath":"Error during the backup operation: the initial path is incorrect!", - "ExceptionMessageTitle":"Error...", - "ExceptionMessageClipboardMessage":"Error text has been copied to the clipboard.", - "ExceptionMessageClipboardButton":"Copy to clipboard", - "ExceptionMessageReportButton":"Report the Problem", - "ExceptionMessageReportMessage":"Please report this error, either with an image of the screen or by copying the following error text (it is appreciable to provide a description of the operations performed before the error):", - "ErrorMessageOpeningWebsite":"Failed to open the web page. Please try again.", - "ConfirmationMessageForClear":"Are you sure you want to clean the fields?", - "ConfirmationMessageForUnsavedChanges":"There are unsaved changes, do you want to save them before moving to another backup?", - "ErrorMessageOpenHistoryFile":"Error opening history file.", - "ConfirmationMessageBeforeDeleteBackup":"Are you sure you want to delete this item? Please note, this action cannot be undone", - "ShareLinkCopiedMessage":"Share link copied to clipboard!", - "ConfirmationMessageCancelAutoBackup":"Are you sure you want to cancel automatic backups for this entry?", - "ErrorMessageUnableToSendEmail":"Unable to send email. Please try again later.", - "ErrorMessageNotSupportedEmail":"Your system does not support sending emails directly from this application.", - "ErrorMessageNotSupportedEmailGeneric":"Your system does not support sending emails.", - "ErrorWrongTimeInterval":"The time interval is not correct", - "AutoBackupActivatedMessage":"Auto Backup has been activated", - "SettedEveryMessage":"\nIs setted every", - "DaysMessage":" days", - "InterruptBackupProcessMessage":"Are you sure you want to stop this backup?", - "ErrorMessageInputMissingGeneric": "Input Missing!", - "ErrorMessageForSavingFile": "Error saving file", - "ErrorMessageForPathNotExisting": "One or both paths do not exist!", - "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.", - "ErrorMessageCountingFiles": "Error occurred while calculating files to back up.", - "ErrorMessageZippingGeneric": "Error occurred while zipping files.", - "ErrorMessageZippingIO": "Error occurred while zipping files: I/O error.", - "ErrorMessageZippingSecurity": "Error occurred while zipping files: Security error.", - "SuccessGenericTitle":"Success", - "SuccessfullyExportedToCsvMessage":"Backups exported to CSV successfully!", - "SuccessfullyExportedToPdfMessage":"Backups exported to PDF successfully!", - "ErrorMessageForExportingToCsv":"Error exporting backups to CSV: ", - "ErrorMessageForExportingToPdf":"Error exporting backups to PDF: ", - "CsvNameMessageInput":"Enter the name of the CSV file.", - "PdfNameMessageInput":"Enter the name of the PDF file.", - "DuplicatedFileNameMessage":"File already exists. Overwrite?", - "ErrorMessageInvalidFilename":"Invalid file name. Use only alphanumeric characters, dashes, and underscores.", - "ConfirmationDeletionTitle":"Confirm Deletion", - "ConfirmationDeletionMessage":"Are you sure you want to delete the selected rows?" - }, - "Subscription": { - "ExpiringTitle": "Backup Manager subscription expiring soon", - "ExpiringMessage": "Your Backup Manager subscription is about to expire.\nAutomatic backups will continue to run until the expiration date.\nPlease contact support to renew it.", - "ExpiredTitle": "Backup Manager subscription expired", - "ExpiredMessage": "Your Backup Manager subscription has expired.\nAutomatic backups will no longer run.\nPlease contact support to reactivate it." - } + "General": { + "AppName": "Backup Manager", + "CancelButton": "Cancel", + "Backup": "Backup", + "Version": "Version", + "ApplyButton": "Apply", + "OkButton": "Ok", + "From": "From", + "SaveButton": "Save", + "CloseButton": "Close", + "CreateButton": "Create", + "EditButton": "Edit", + "DeleteButton": "Delete", + "QuickSearch": "Quick search", + "ContactUs": "Contact us", + "To": "A" + }, + "TrayIcon": { + "SuccessMessage": "\nThe backup was successfully completed:", + "ExitAction": "Exit", + "ErrorMessageFilesNotExisting": "\nError during automatic backup.\nOne or both paths do not exist!", + "TrayTooltip": "Backup Service", + "OpenAction": "Quick Access", + "ErrorMessageInputMissing": "\nError during automatic backup.\nInput Missing!", + "ErrorMessageSamePaths": "\nError during automatic backup.\nThe initial path and destination path cannot be the same. Please choose different paths!" + }, + "Dialogs": { + "WarningGenericTitle": "Warning", + "ErrorMessageForPathNotExisting": "One or both paths do not exist!", + "ExceptionMessageReportMessage": "Please report this error, either with an image of the screen or by copying the following error text (it is appreciable to provide a description of the operations performed before the error):", + "ErrorGenericTitle": "Error", + "ErrorMessageForSamePaths": "The initial path and destination path cannot be the same. Please choose different paths!", + "SettedEveryMessage": "\nIs setted every", + "WarningBackupAlreadyInProgressMessage": "There is already a backup in progress. It is not possible to perform parallel backups", + "WarningShortTimeIntervalMessage": "The selected time interval is very short. For optimal performance, we recommend setting it to at least one hour. Do you still want to proceed?", + "ConfirmationMessageCancelAutoBackup": "Are you sure you want to cancel automatic backups for this entry?", + "ErrorMessageCountingFiles": "Error occurred while calculating files to back up.", + "ErrorMessageInputMissingGeneric": "Input Missing!", + "BackupNameInput": "Name of the backup", + "CsvNameMessageInput": "Enter the name of the CSV file.", + "ErrorMessageZippingGeneric": "Error occurred while zipping files.", + "ErrorMessageZippingIO": "Error occurred while zipping files: I/O error.", + "DuplicatedFileNameMessage": "File already exists. Overwrite?", + "AutoBackupActivatedMessage": "Auto Backup has been activated", + "AutoBackup": "Auto Backup", + "DaysMessage": " days", + "ExceptionMessageReportButton": "Report the Problem", + "SuccessGenericTitle": "Success", + "DuplicatedBackupNameMessage": "A backup with the same name already exists, do you want to overwrite it?", + "ExceptionMessageClipboardButton": "Copy to clipboard", + "ErrorMessageZippingSecurity": "Error occurred while zipping files: Security error.", + "InterruptBackupProcessMessage": "Are you sure you want to stop this backup?", + "ConfirmationMessageBeforeDeleteBackup": "Are you sure you want to delete this item? Please note, this action cannot be undone", + "ConfirmationRequiredTitle": "Confirmation required" + }, + "TimePickerDialog": { + "TimeIntervalTitle": "Time interval for auto backup", + "Days": "Days", + "Hours": "Hours", + "SpinnerTooltip": "Mouse wheel to adjust the value", + "Description": "Select how often to perform the automatic backup by \nchoosing the frequency in days, hours, and minutes.", + "Minutes": "Minutes" + }, + "Subscription": { + "ExpiredTitle": "Backup Manager subscription expired", + "ExpiringMessage": "Your Backup Manager subscription is about to expire.\nAutomatic backups will continue to run until the expiration date.\nPlease contact support to renew it.", + "ExpiringTitle": "Backup Manager subscription expiring soon", + "ExpiredMessage": "Your Backup Manager subscription has expired.\nAutomatic backups will no longer run.\nPlease contact support to reactivate it.", + "ActiveLabel": "Active", + "ExpiringLabel": "Expiring", + "ExpiredLabel": "Expired", + "Status": "Subscription status", + "ValidFrom": "Valid from", + "ValidTo": "Valid to", + "ToExtend": "to extend the subscription period." + }, + "ProgressBackupFrame": { + "StatusLoading": "Loading...", + "ProgressBackupTitle": "Backup in progress", + "StatusCompleted": "Backup completed!" + }, + "Menu": { + "About": "About", + "File": "File", + "BugReport": "Report a bug", + "Options": "Options", + "New": "New", + "Help": "Help", + "History": "History", + "SubmenuMain": "MAIN", + "SubmenuOther": "OTHER", + "Donate": "Support the project", + "Backups": "Backup List", + "ImportBackup": "Import backups from Csv", + "ExportBackup": "Export backups to Csv", + "Dashboard": "Dashboard", + "Github": "Github page", + "Paypal": "Paypal", + "BuyMeACoffe": "Buy me a coffe", + "Subscription": "Subscription" + }, + "BackupList": { + "NotesDetail": "Notes", + "BackupNameDetail": "BackupName", + "EditPopup": "Edit", + "ResearchBarTooltip": "Research bar", + "InitialPathDetail": "InitialPath", + "RenameBackupPopup": "Rename backup", + "NextBackupDateDetail": "NextBackup", + "AutoBackupPopup": "Auto backup", + "BackupNameColumn": "Backup Name", + "BackupPopup": "Backup", + "BackupCountDetail": "BackupCount", + "InitialPathColumn": "Initial Path", + "MaxBackupsToKeepDetail": "MaxBackupsToKeep", + "DeletePopup": "Delete", + "OpenDestinationFolderPopup": "Open destination path", + "LastBackupColumn": "Last Backup", + "SingleBackupPopup": "Run single backup", + "CopyTextPopup": "Copy text", + "DestinationPathDetail": "DestinationPath", + "CopyDestinationPathPopup": "Copy destination path", + "LastBackupDetail": "LastBackup", + "OpenInitialFolderPopup": "Open initial path", + "ResearchBarPlaceholder": "Search...", + "InterruptPopup": "Interrupt", + "DuplicatePopup": "Duplicate", + "NextBackupDateColumn": "Next Backup Date", + "DestinationPathColumn": "Destination Path", + "AutomaticBackupColumn": "Automatic Backup", + "LastUpdateDateDetail": "LastUpdateDate", + "TimeIntervalDetail": "TimeInterval", + "CopyBackupNamePopup": "Copy backup name", + "CreationDateDetail": "CreationDate", + "CopyInitialPathPopup": "Copy initial path", + "BackupListTitle": "Backup List", + "BackupListDescription": "Manage and monitor backup configurations, including creation, editing, scheduling, and execution.", + "TimeIntervalColumn": "Interval (gg.HH:mm)", + "MaxBackupsColumn": "Max Backups To Keep" + }, + "UserDialog": { + "EmailConfirmationBody": "Hi [UserName],\n\nThank you for downloading and registering Backup Manager, your new tool for secure and efficient backup management!\n\nThis is an automated email sent to confirm your registration. We will contact you by email only to inform you about new releases or important updates of the application.\n\nIn the meantime, if you have any questions, need assistance, or have suggestions, we are always here for you. You can reach us at [SupportEmail].\n\nThank you again for choosing Backup Manager, and enjoy managing your backups!\n\nBest regards,\nThe Backup Manager Team", + "EmailConfirmationSubject": "Thank you for choosing Backup Manager!", + "Surname": "Surname", + "Name": "Name", + "Email": "Email", + "UserTitle": "Insert your data", + "UserDescription": "Please enter your data to access the system", + "UserNamePlaceholder": "Enter your name", + "UserSurnamePlaceholder": "Enter your surname", + "UserEmailPlaceholder": "Enter your email" + }, + "BackupEntry": { + "TimePickerTooltip": "Time picker", + "MaxBackupsToKeepTooltip": "Maximum number of backups before removing the oldest.", + "BackupName": "Backup name", + "InitialPathTooltip": "(Required) Initial path", + "InitialFileChooserTooltip": "Open file explorer", + "LastBackup": "Last backup", + "SingleBackupButton": "Single Backup", + "AutoBackupButtonON": "Auto Backup (ON)", + "AutoBackupButtonOFF": "Auto Backup (OFF)", + "DestinationPathTooltip": "(Required) Destination path", + "MaxBackupsToKeep": "Max backups to keep", + "SingleBackupTooltip": "Perform the backup", + "AutoBackupTooltip": "Enable/Disable automatic backup", + "NotesTooltip": "(Optional) Backup description", + "DestinationFileChooserTooltip": "Open file explorer", + "BackupNameTooltip": "(Required) Backup name", + "Notes": "Notes", + "PageSubtitleCreate": "Create Backup", + "PageSubtitleEdit": "Edit Backup", + "PageSubtitleInfo": "Backup Information", + "PageSubtitleSettings": "Backups Settings", + "Paths": "Paths", + "BackupNamePlaceholder": "Backup name (unique)", + "InitialPathPlaceholder": "Target path e.g. C:\\Users\\Admin\\Documents", + "DestinationPathPlaceholder": "Destination folder e.g. D:\\Backups" + }, + "HistoryLogs": { + "HistoryLogsTitle": "History logs", + "HistoryLogsDescription": "Here you can find the application logs, useful for troubleshooting and understanding the application's behavior over time." + }, + "About": { + "AboutSystemInformation": "System Information", + "AboutMessageBody": "Backup Manager is a simple and powerful application designed to automate folder and subfolder backups.

Users can schedule automatic backups or execute manual backups anytime.

Backup history is stored securely, allowing full control over saved data.

Visit project website for more information.

" + }, + "Dashboard": { + "DashboardTitle": "Backup Analytics Dashboard", + "DashboardCardTotalConfigurations": "Total Backup Configurations", + "DashboardCardTotalExecutions": "Total Backup Executions", + "DashboardCardSuccessRate": "Success rate", + "DashboardCardAvgDuration": "Avg Backup Duration", + "DashboardCardCompressionRate": "Compression Rate", + "DashboardChartExecutions": "Backup Executions", + "DashboardChartAvgDuration": "Average Backup Duration (min)" + }, + "Settings": { + "SettingsLayoutTab": "Layout", + "SettingsStyleTab": "Style", + "SettingsWindowsLayout": "Windows Layout", + "SettingsWindowsRight": "Right to Left", + "SettingsWindowsFull": "Full Window Content", + "SettingsDrawerLayout": "Drawer layout", + "SettingsDrawerLeft": "Left", + "SettingsDrawerLeading": "Leading", + "SettingsDrawerTrailing": "Trailing", + "SettingsDrawerRight": "Right", + "SettingsDrawerTop": "Top", + "SettingsDrawerBottom": "Bottom", + "SettingsModalOption": "Default modal option", + "SettingsModalAnimation": "Animation enable", + "SettingsModalClose": "Close on pressed escape", + "SettingsLanguagesLayout": "Language", + "SettingsAccentLayout": "Accent color", + "SettingsColorPickerLayout": "Color Picker", + "SettingsDrawerLineLayout": "Drawer line style", + "SettingsDrawerLineCurved": "Curved line style", + "SettingsDrawerDotLine": "Straight dot line style", + "SettingsLineStyleLayout": "Line style option", + "SettingsLineStyleRettangle": "Rettangle", + "SettingsLineStyleEllipse": "Ellipse", + "SettingsLineStyleLine": "Line", + "SettingsLineStyleCurved": "Curved", + "SettingsColorOptionLayout": "Color option", + "SettingsColorOptionPainted": "Paint selected line color", + "SettingsThemes": "Themes", + "SettingsThemeAll": "All", + "SettingsThemeLight": "Light", + "SettingsThemeDark": "Dark" + }, + "SearchBar": { + "SearcTitle": "Search...", + "SearchNoRecent": "No recent searches", + "SearchNoResult": "No result for", + "SearchFavorite": "Favorite", + "SearchRecent": "Recents" + }, + "Toast": { + "BackupEditedOk": "Backup updated successfully", + "BackupCreatedOk": "Backup created successfully", + "BackupDeletedOk": "Backup deleted successfully", + "BackupReplacedOk": "Existing backup replaced successfully", + "BackupDeletedError": "Failed to delete the backup", + "BackupReplacedError": "Failed to replace the backup", + "InvalidTime": "Invalid time interval", + "SubscriptionExpiring": "Your Backup Manager subscription will expire soon. Please contact us to renew it", + "SubscriptionExpired": "Your Backup Manager subscription has expired. Please contact us to renew it", + "LanguageChange": "Some changes will take effect after restarting the application", + "MissingDataLoginError": "Please fill in all the required fields", + "WrongEmailLoginError": "The provided email address is invalid. Please provide a correct one", + "LoginOk": "Welcome! You have successfully signed in", + "ErrorTextClipboard": "Error text has been copied to the clipboard", + "CsvExport": "Backups exported to CSV successfully!", + "CsvExportError": "Error exporting backups to CSV", + "CsvExportInvalidFilename": "Invalid file name. Use only alphanumeric characters, dashes, and underscores", + "NotSupportedEmail": "Your system does not support sending emails directly from this application", + "UnableToSendEmail": "Unable to send email. Please try again later", + "NotSupportedEmailGeneric": "Your system does not support sending emails", + "OpeningWebsiteError": "Failed to open the web page. Please try again", + "FolderNotExisting": "The folder does not exist or is invalid", + "BackupNameAlreadyUsed": "Backup name already used!", + "BackupAlreadyInProgress": "There is already a backup in progress. It is not possible to perform parallel backups", + "BackupPathsEmptyError": "The initial path and destination path cannot be empty!" + } } diff --git a/src/main/resources/res/languages/esp.json b/src/main/resources/res/languages/esp.json index b5ba7f0f..4e6e4a2d 100644 --- a/src/main/resources/res/languages/esp.json +++ b/src/main/resources/res/languages/esp.json @@ -1,208 +1,267 @@ { - "General": { - "AppName": "Backup Manager", - "Backup": "Copia de Seguridad", - "Version": "Versión", - "From": "Desde", - "To": "A", - "OkButton": "Aceptar", - "CancelButton": "Cancelar", - "CloseButton": "Cerrar", - "ApplyButton": "Aplicar", - "SaveButton": "Guardar", - "CreateButton": "Crear" - }, - "Menu": { - "File": "Archivo", - "Options": "Opciones", - "About": "Acerca de", - "Help": "Ayuda", - "BugReport": "Reportar un error", - "Clear": "Limpiar", - "Donate": "Donar", - "History": "Historial", - "InfoPage": "Información", - "New": "Nuevo", - "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" - }, - "TabbedFrames": { - "BackupEntry": "Entrada de Copia de Seguridad", - "BackupList": "Lista de Copias de Seguridad" - }, - "BackupEntry": { - "PageTitle": "Entrada de Copia de Seguridad", - "CurrentFile": "Archivo actual", - "Notes": "Notas", - "LastBackup": "Última copia de seguridad", - "SingleBackupButton": "Copia de Seguridad Única", - "AutoBackupButton": "Copia de Seguridad Automática", - "AutoBackupButtonON": "Copia de Seguridad Automática (ACTIVADA)", - "AutoBackupButtonOFF": "Copia de Seguridad Automática (DESACTIVADA)", - "InitialPathPlaceholder": "Ruta inicial", - "DestinationPathPlaceholder": "Ruta de destino", - "BackupName": "Nombre de la copia de seguridad", - "BackupNameTooltip": "(Requerido) Nombre de la copia de seguridad", - "InitialPathTooltip": "(Requerido) Ruta inicial", - "DestinationPathTooltip": "(Requerido) Ruta de destino", - "InitialFileChooserTooltip": "Abrir explorador de archivos", - "DestinationFileChooserTooltip": "Abrir explorador de archivos", - "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", - "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", - "InitialPathColumn": "Ruta Inicial", - "DestinationPathColumn": "Ruta de Destino", - "LastBackupColumn": "Última Copia de Seguridad", - "AutomaticBackupColumn": "Copia Automática", - "NextBackupDateColumn": "Próxima Fecha de Copia", - "TimeIntervalColumn": "Intervalo de Tiempo", - "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", - "ExportAs": "Exportar como: ", - "ExportAsPdfTooltip": "Exportar como PDF", - "ExportAsCsvTooltip": "Exportar como CSV", - "ResearchBarTooltip": "Barra de búsqueda", - "ResearchBarPlaceholder": "Buscar...", - "EditPopup": "Editar", - "DeletePopup": "Eliminar", - "InterruptPopup": "Interrumpir", - "DuplicatePopup": "Duplicar", - "RenameBackupPopup": "Renombrar copia de seguridad", - "OpenInitialFolderPopup": "Abrir ruta inicial", - "OpenDestinationFolderPopup": "Abrir ruta de destino", - "BackupPopup": "Copia de seguridad", - "SingleBackupPopup": "Ejecutar copia única", - "AutoBackupPopup": "Copia automática", - "CopyTextPopup": "Copiar texto", - "CopyBackupNamePopup": "Copiar nombre de copia", - "CopyInitialPathPopup": "Copiar ruta inicial", - "CopyDestinationPathPopup": "Copiar ruta de destino" - }, - "TimePickerDialog": { - "TimeIntervalTitle": "Intervalo de tiempo para copias automáticas", - "Description": "Seleccione la frecuencia para realizar la copia automática \neligiendo los días, horas y minutos.", - "Days": "Días", - "Hours": "Horas", - "Minutes": "Minutos", - "SpinnerTooltip": "Rueda del ratón para ajustar el valor" - }, - "PreferencesDialog": { - "PreferencesTitle": "Preferencias", - "Language": "Idioma", - "Theme": "Tema" - }, - "UserDialog": { - "UserTitle": "Inserta tus datos", - "Name": "Nombre", - "Surname": "Apellido", - "Email": "Correo electrónico", - "ErrorMessageForMissingData": "Por favor, completa todos los campos requeridos.", - "ErrorMessageForWrongEmail": "La dirección de correo electrónico proporcionada no es válida. Por favor, proporciona una correcta.", - "EmailConfirmationSubject": "¡Gracias por elegir Backup Manager!", - "EmailConfirmationBody": "Hola [UserName],\n\n¡Gracias por descargar y registrar Backup Manager, tu nueva herramienta para una gestión segura y eficiente de tus copias de seguridad!\n\nEste es un correo automático enviado para confirmar tu registro. Nos pondremos en contacto contigo por correo solo para informarte sobre nuevas versiones o actualizaciones importantes de la aplicación.\n\nMientras tanto, si tienes preguntas, necesitas ayuda o tienes sugerencias, estamos siempre a tu disposición. Puedes contactarnos en [SupportEmail].\n\n¡Gracias nuevamente por elegir Backup Manager y disfruta gestionando tus copias de seguridad!\n\nSaludos cordiales,\nEl equipo de Backup Manager" - }, - "ProgressBackupFrame": { - "ProgressBackupTitle": "Copia de Seguridad en Progreso", - "StatusCompleted": "¡Copia completada!", - "StatusLoading": "Cargando..." - }, - "TrayIcon": { - "TrayTooltip": "Servicio de Copias de Seguridad", - "OpenAction": "Acceso Rápido", - "ExitAction": "Salir", - "SuccessMessage": "\nLa copia de seguridad se completó con éxito:", - "ErrorMessageInputMissing": "\nError en la copia automática.\n¡Faltan datos de entrada!", - "ErrorMessageFilesNotExisting": "\nError en la copia automática.\n¡Una o ambas rutas no existen!", - "ErrorMessageSamePaths": "\nError en la copia automática.\nLa ruta inicial y la de destino no pueden ser iguales. ¡Elija rutas diferentes!" - }, - "Dialogs": { - "ErrorGenericTitle": "Error", - "WarningGenericTitle": "Advertencia", - "WarningBackupAlreadyInProgressMessage": "Ya hay una copia de seguridad en progreso. No es posible realizar copias de seguridad en paralelo.", - "WarningShortTimeIntervalMessage": "El intervalo de tiempo seleccionado es muy corto. Para un funcionamiento óptimo, recomendamos configurarlo en al menos una hora. ¿Quieres continuar de todos modos?", - "ErrorMessageForFolderNotExisting": "La carpeta no existe o no es válida", - "ErrorMessageForSavingFileWithPathsEmpty": "No se puede guardar el archivo. Tanto la ruta inicial como la de destino deben especificarse y no pueden estar vacías", - "BackupSavedCorrectlyTitle": "Copia Guardada", - "BackupSavedCorrectlyMessage": "guardada con éxito.", - "ErrorSavingBackupMessage": "Error al guardar la copia de seguridad", - "BackupNameInput": "Nombre de la copia", - "ConfirmationRequiredTitle": "Confirmación requerida", - "DuplicatedBackupNameMessage": "Ya existe una copia con el mismo nombre. ¿Desea sobrescribirla?", - "BackupNameAlreadyUsedMessage": "¡Nombre de copia ya en uso!", - "ErrorMessageForIncorrectInitialPath": "Error en la operación de copia: ¡la ruta inicial es incorrecta!", - "ExceptionMessageTitle": "Error...", - "ExceptionMessageClipboardMessage": "El texto del error se ha copiado al portapapeles.", - "ExceptionMessageClipboardButton": "Copiar al portapapeles", - "ExceptionMessageReportButton": "Informar del problema", - "ExceptionMessageReportMessage": "Por favor, informe de este error, ya sea con una captura de pantalla o copiando el siguiente texto del error (se agradece proporcionar una descripción de las acciones realizadas antes del error):", - "ErrorMessageOpeningWebsite": "No se pudo abrir la página web. Por favor, intente de nuevo.", - "ConfirmationMessageForClear": "¿Está seguro de que desea limpiar los campos?", - "ConfirmationMessageForUnsavedChanges": "Hay cambios no guardados. ¿Desea guardarlos antes de pasar a otra copia de seguridad?", - "ErrorMessageOpenHistoryFile": "Error al abrir el archivo de historial.", - "ConfirmationMessageBeforeDeleteBackup": "¿Está seguro de que desea eliminar este elemento? Tenga en cuenta que esta acción no se puede deshacer.", - "ShareLinkCopiedMessage": "¡Enlace de compartir copiado al portapapeles!", - "ConfirmationMessageCancelAutoBackup": "¿Está seguro de que desea cancelar las copias automáticas para esta entrada?", - "ErrorMessageUnableToSendEmail": "No se pudo enviar el correo electrónico. Por favor, intente de nuevo más tarde.", - "ErrorMessageNotSupportedEmail": "Su sistema no admite el envío de correos electrónicos directamente desde esta aplicación.", - "ErrorMessageNotSupportedEmailGeneric": "Su sistema no admite el envío de correos electrónicos.", - "ErrorWrongTimeInterval": "El intervalo de tiempo no es correcto", - "AutoBackupActivatedMessage": "La copia automática se ha activado", - "SettedEveryMessage": "\nSe establece cada", - "DaysMessage": " días", - "InterruptBackupProcessMessage": "¿Está seguro de que desea detener esta copia?", - "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!", - "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: Seleccione un archivo JSON válido.", - "ErrorMessageCountingFiles": "Error al calcular los archivos para la copia de seguridad.", - "ErrorMessageZippingGeneric": "Error al comprimir los archivos.", - "ErrorMessageZippingIO": "Error al comprimir los archivos: error de E/S.", - "ErrorMessageZippingSecurity": "Error al comprimir los archivos: Error de seguridad.", - "SuccessGenericTitle": "Éxito", - "SuccessfullyExportedToCsvMessage": "¡Copias de seguridad exportadas a CSV con éxito!", - "SuccessfullyExportedToPdfMessage": "¡Copias de seguridad exportadas a PDF con éxito!", - "ErrorMessageForExportingToCsv": "Error al exportar copias de seguridad a CSV: ", - "ErrorMessageForExportingToPdf": "Error al exportar copias de seguridad a PDF: ", - "CsvNameMessageInput": "Introduce el nombre del archivo CSV.", - "PdfNameMessageInput": "Introduce el nombre del archivo PDF.", - "DuplicatedFileNameMessage": "El archivo ya existe. ¿Sobrescribir?", - "ErrorMessageInvalidFilename": "Nombre de archivo no válido. Usa solo caracteres alfanuméricos, guiones y guiones bajos.", - "ConfirmationDeletionTitle": "Confirmar Eliminación", - "ConfirmationDeletionMessage": "¿Está seguro de que desea eliminar las filas seleccionadas?" - }, - "Subscription": { - "ExpiringTitle": "La suscripción de Backup Manager está por vencer", - "ExpiringMessage": "Tu suscripción a Backup Manager está a punto de expirar.\nLas copias de seguridad automáticas seguirán funcionando hasta la fecha de vencimiento.\nContacta con el soporte para renovarla.", - "ExpiredTitle": "La suscripción de Backup Manager ha expirado", - "ExpiredMessage": "Tu suscripción a Backup Manager ha expirado.\nLas copias de seguridad automáticas ya no se ejecutarán.\nContacta con el soporte para reactivarla." - } + "General": { + "AppName": "Backup Manager", + "CancelButton": "Cancelar", + "Backup": "Copia de Seguridad", + "Version": "Versión", + "ApplyButton": "Aplicar", + "OkButton": "Aceptar", + "From": "Desde", + "SaveButton": "Guardar", + "CloseButton": "Cerrar", + "CreateButton": "Crear", + "EditButton": "Editar", + "DeleteButton": "Eliminar", + "QuickSearch": "Búsqueda rápida", + "ContactUs": "Contáctanos", + "To": "A" + }, + "TrayIcon": { + "SuccessMessage": "\nLa copia de seguridad se completó con éxito:", + "ExitAction": "Salir", + "ErrorMessageFilesNotExisting": "\nError en la copia automática.\n¡Una o ambas rutas no existen!", + "TrayTooltip": "Servicio de Copias de Seguridad", + "OpenAction": "Acceso Rápido", + "ErrorMessageInputMissing": "\nError en la copia automática.\n¡Faltan datos de entrada!", + "ErrorMessageSamePaths": "\nError en la copia automática.\nLa ruta inicial y la de destino no pueden ser iguales. ¡Elija rutas diferentes!" + }, + "Dialogs": { + "WarningGenericTitle": "Advertencia", + "ErrorMessageForPathNotExisting": "¡Una o ambas rutas no existen!", + "ExceptionMessageReportMessage": "Por favor, informe de este error, ya sea con una captura de pantalla o copiando el siguiente texto del error (se agradece proporcionar una descripción de las acciones realizadas antes del error):", + "ErrorGenericTitle": "Error", + "ErrorMessageForSamePaths": "La ruta inicial y la de destino no pueden ser iguales. ¡Elija rutas diferentes!", + "SettedEveryMessage": "\nSe establece cada", + "WarningBackupAlreadyInProgressMessage": "Ya hay una copia de seguridad en progreso. No es posible realizar copias de seguridad en paralelo.", + "WarningShortTimeIntervalMessage": "El intervalo de tiempo seleccionado es muy corto. Para un funcionamiento óptimo, recomendamos configurarlo en al menos una hora. ¿Quieres continuar de todos modos?", + "ConfirmationMessageCancelAutoBackup": "¿Está seguro de que desea cancelar las copias automáticas para esta entrada?", + "ErrorMessageCountingFiles": "Error al calcular los archivos para la copia de seguridad.", + "ErrorMessageInputMissingGeneric": "¡Faltan datos de entrada!", + "BackupNameInput": "Nombre de la copia", + "CsvNameMessageInput": "Introduce el nombre del archivo CSV.", + "ErrorMessageZippingGeneric": "Error al comprimir los archivos.", + "ErrorMessageZippingIO": "Error al comprimir los archivos: error de E/S.", + "DuplicatedFileNameMessage": "El archivo ya existe. ¿Sobrescribir?", + "AutoBackupActivatedMessage": "La copia automática se ha activado", + "AutoBackup": "Copia de seguridad automática", + "DaysMessage": " días", + "ExceptionMessageReportButton": "Informar del problema", + "SuccessGenericTitle": "Éxito", + "DuplicatedBackupNameMessage": "Ya existe una copia con el mismo nombre. ¿Desea sobrescribirla?", + "ExceptionMessageClipboardButton": "Copiar al portapapeles", + "ErrorMessageZippingSecurity": "Error al comprimir los archivos: Error de seguridad.", + "InterruptBackupProcessMessage": "¿Está seguro de que desea detener esta copia?", + "ConfirmationMessageBeforeDeleteBackup": "¿Está seguro de que desea eliminar este elemento? Tenga en cuenta que esta acción no se puede deshacer.", + "ConfirmationRequiredTitle": "Confirmación requerida" + }, + "TimePickerDialog": { + "TimeIntervalTitle": "Intervalo de tiempo para copias automáticas", + "Days": "Días", + "Hours": "Horas", + "SpinnerTooltip": "Rueda del ratón para ajustar el valor", + "Description": "Seleccione la frecuencia para realizar la copia automática \neligiendo los días, horas y minutos.", + "Minutes": "Minutos" + }, + "Subscription": { + "ExpiredTitle": "La suscripción de Backup Manager ha expirado", + "ExpiringMessage": "Tu suscripción a Backup Manager está a punto de expirar.\nLas copias de seguridad automáticas seguirán funcionando hasta la fecha de vencimiento.\nContacta con el soporte para renovarla.", + "ExpiringTitle": "La suscripción de Backup Manager está por vencer", + "ExpiredMessage": "Tu suscripción a Backup Manager ha expirado.\nLas copias de seguridad automáticas ya no se ejecutarán.\nContacta con el soporte para reactivarla.", + "ActiveLabel": "Activo", + "ExpiringLabel": "Próximo a expirar", + "ExpiredLabel": "Expirado", + "Status": "Estado de la suscripción", + "ValidFrom": "Válido desde", + "ValidTo": "Válido hasta", + "ToExtend": "para extender el período de suscripción." + }, + "ProgressBackupFrame": { + "StatusLoading": "Cargando...", + "ProgressBackupTitle": "Copia de Seguridad en Progreso", + "StatusCompleted": "¡Copia completada!" + }, + "Menu": { + "About": "Acerca de", + "File": "Archivo", + "BugReport": "Reportar un error", + "Options": "Opciones", + "New": "Nuevo", + "Help": "Ayuda", + "History": "Historial", + "SubmenuMain": "PRINCIPAL", + "SubmenuOther": "OTROS", + "Donate": "Apoya el proyecto", + "Backups": "Lista de copias de seguridad", + "ImportBackup": "Importar copias de seguridad desde CSV", + "ExportBackup": "Exportar copias de seguridad a CSV", + "Dashboard": "Panel", + "Github": "Página de GitHub", + "Paypal": "PayPal", + "BuyMeACoffe": "Invítame a un café", + "Subscription": "Suscripción" + }, + "BackupList": { + "NotesDetail": "Notas", + "BackupNameDetail": "NombreCopia", + "EditPopup": "Editar", + "ResearchBarTooltip": "Barra de búsqueda", + "InitialPathDetail": "RutaInicial", + "RenameBackupPopup": "Renombrar copia de seguridad", + "NextBackupDateDetail": "PróximaFecha", + "AutoBackupPopup": "Copia automática", + "BackupNameColumn": "Nombre de la Copia de Seguridad", + "BackupPopup": "Copia de seguridad", + "BackupCountDetail": "NúmeroCopias", + "InitialPathColumn": "Ruta Inicial", + "MaxBackupsToKeepDetail": "MaximoCopiasDeSeguridadMantener", + "DeletePopup": "Eliminar", + "OpenDestinationFolderPopup": "Abrir ruta de destino", + "LastBackupColumn": "Última Copia de Seguridad", + "SingleBackupPopup": "Ejecutar copia única", + "CopyTextPopup": "Copiar texto", + "DestinationPathDetail": "RutaDestino", + "CopyDestinationPathPopup": "Copiar ruta de destino", + "LastBackupDetail": "ÚltimaCopia", + "OpenInitialFolderPopup": "Abrir ruta inicial", + "ResearchBarPlaceholder": "Buscar...", + "InterruptPopup": "Interrumpir", + "DuplicatePopup": "Duplicar", + "NextBackupDateColumn": "Próxima Fecha de Copia", + "DestinationPathColumn": "Ruta de Destino", + "AutomaticBackupColumn": "Copia Automática", + "LastUpdateDateDetail": "ÚltimaActualización", + "TimeIntervalDetail": "IntervaloTiempo", + "CopyBackupNamePopup": "Copiar nombre de copia", + "CreationDateDetail": "FechaCreación", + "CopyInitialPathPopup": "Copiar ruta inicial", + "BackupListTitle": "Lista de copias de seguridad", + "BackupListDescription": "Gestiona y supervisa las configuraciones de copia de seguridad, incluyendo creación, edición, programación y ejecución.", + "TimeIntervalColumn": "Intervalo (dd.HH:mm)", + "MaxBackupsColumn": "Número máximo de copias a conservar" + }, + "UserDialog": { + "EmailConfirmationBody": "Hola [UserName],\n\n¡Gracias por descargar y registrar Backup Manager, tu nueva herramienta para una gestión segura y eficiente de tus copias de seguridad!\n\nEste es un correo automático enviado para confirmar tu registro. Nos pondremos en contacto contigo por correo solo para informarte sobre nuevas versiones o actualizaciones importantes de la aplicación.\n\nMientras tanto, si tienes preguntas, necesitas ayuda o tienes sugerencias, estamos siempre a tu disposición. Puedes contactarnos en [SupportEmail].\n\n¡Gracias nuevamente por elegir Backup Manager y disfruta gestionando tus copias de seguridad!\n\nSaludos cordiales,\nEl equipo de Backup Manager", + "EmailConfirmationSubject": "¡Gracias por elegir Backup Manager!", + "Surname": "Apellido", + "Name": "Nombre", + "Email": "Correo electrónico", + "UserTitle": "Inserta tus datos", + "UserDescription": "Por favor introduce tus datos para acceder al sistema", + "UserNamePlaceholder": "Introduce tu nombre", + "UserSurnamePlaceholder": "Introduce tus apellidos", + "UserEmailPlaceholder": "Introduce tu correo electrónico" + }, + "BackupEntry": { + "TimePickerTooltip": "Selector de tiempo", + "MaxBackupsToKeepTooltip": "Número máximo de copias de seguridad antes de eliminar las más antiguas.", + "BackupName": "Nombre de la copia de seguridad", + "InitialPathTooltip": "(Requerido) Ruta inicial", + "InitialFileChooserTooltip": "Abrir explorador de archivos", + "LastBackup": "Última copia de seguridad", + "SingleBackupButton": "Copia de Seguridad Única", + "AutoBackupButtonON": "Copia de Seguridad Automática (ACTIVADA)", + "AutoBackupButtonOFF": "Copia de Seguridad Automática (DESACTIVADA)", + "DestinationPathTooltip": "(Requerido) Ruta de destino", + "MaxBackupsToKeep": "Máximo de copias de seguridad a mantener", + "SingleBackupTooltip": "Realizar la copia de seguridad", + "AutoBackupTooltip": "Activar/Desactivar copia de seguridad automática", + "NotesTooltip": "(Opcional) Descripción de la copia de seguridad", + "DestinationFileChooserTooltip": "Abrir explorador de archivos", + "BackupNameTooltip": "(Requerido) Nombre de la copia de seguridad", + "Notes": "Notas", + "PageSubtitleCreate": "Crear copia de seguridad", + "PageSubtitleEdit": "Editar copia de seguridad", + "PageSubtitleInfo": "Información de la copia de seguridad", + "PageSubtitleSettings": "Configuración de copias de seguridad", + "Paths": "Rutas", + "BackupNamePlaceholder": "Nombre de la copia de seguridad (único)", + "InitialPathPlaceholder": "Ruta de origen p. ej. C:\\Users\\Admin\\Documents", + "DestinationPathPlaceholder": "Carpeta de destino p. ej. D:\\Backups" + }, + "HistoryLogs": { + "HistoryLogsTitle": "Registros del historial", + "HistoryLogsDescription": "Aquí puedes encontrar los registros de la aplicación, útiles para la resolución de problemas y para comprender el comportamiento de la aplicación a lo largo del tiempo." + }, + "About": { + "AboutSystemInformation": "Información del sistema", + "AboutMessageBody": "Backup Manager es una aplicación simple y potente diseñada para automatizar las copias de seguridad de carpetas y subcarpetas.

Los usuarios pueden programar copias automáticas o ejecutar copias manuales en cualquier momento.

El historial de copias se almacena de forma segura, permitiendo un control total sobre los datos guardados.

Visita el sitio web del proyecto para más información.

" + }, + "Dashboard": { + "DashboardTitle": "Panel analítico de backups", + "DashboardCardTotalConfigurations": "Configuraciones de backup totales", + "DashboardCardTotalExecutions": "Ejecuciones de backup totales", + "DashboardCardSuccessRate": "Tasa de éxito", + "DashboardCardAvgDuration": "Duración media del backup", + "DashboardCardCompressionRate": "Tasa de compresión", + "DashboardChartExecutions": "Ejecuciones de backup", + "DashboardChartAvgDuration": "Duración media del backup (min)" + }, + "Settings": { + "SettingsLayoutTab": "Diseño", + "SettingsStyleTab": "Estilo", + "SettingsWindowsLayout": "Diseño de ventana", + "SettingsWindowsRight": "De derecha a izquierda", + "SettingsWindowsFull": "Contenido completo de ventana", + "SettingsDrawerLayout": "Diseño del panel", + "SettingsDrawerLeft": "Izquierda", + "SettingsDrawerLeading": "Inicio", + "SettingsDrawerTrailing": "Final", + "SettingsDrawerRight": "Derecha", + "SettingsDrawerTop": "Arriba", + "SettingsDrawerBottom": "Abajo", + "SettingsModalOption": "Opción modal predeterminada", + "SettingsModalAnimation": "Activar animación", + "SettingsModalClose": "Cerrar al presionar ESC", + "SettingsLanguagesLayout": "Idioma", + "SettingsAccentLayout": "Color de acento", + "SettingsColorPickerLayout": "Selector de color", + "SettingsDrawerLineLayout": "Estilo de línea del panel", + "SettingsDrawerLineCurved": "Línea curva", + "SettingsDrawerDotLine": "Línea punteada recta", + "SettingsLineStyleLayout": "Opción estilo de línea", + "SettingsLineStyleRettangle": "Rectángulo", + "SettingsLineStyleEllipse": "Elipse", + "SettingsLineStyleLine": "Línea", + "SettingsLineStyleCurved": "Curva", + "SettingsColorOptionLayout": "Opción de color", + "SettingsColorOptionPainted": "Colorear la línea seleccionada", + "SettingsThemes": "Temas", + "SettingsThemeAll": "Todos", + "SettingsThemeLight": "Claro", + "SettingsThemeDark": "Oscuro" + }, + "SearchBar": { + "SearcTitle": "Buscar...", + "SearchNoRecent": "No hay búsquedas recientes", + "SearchNoResult": "Sin resultados para", + "SearchFavorite": "Favorito", + "SearchRecent": "Recientes" + }, + "Toast": { + "BackupEditedOk": "Copia de seguridad actualizada correctamente", + "BackupCreatedOk": "Copia de seguridad creada correctamente", + "BackupDeletedOk": "Copia de seguridad eliminada correctamente", + "BackupReplacedOk": "Copia de seguridad existente reemplazada correctamente", + "BackupDeletedError": "No se pudo eliminar la copia de seguridad", + "BackupReplacedError": "No se pudo reemplazar la copia de seguridad", + "InvalidTime": "Intervalo de tiempo no válido", + "SubscriptionExpiring": "Tu suscripción a Backup Manager expirará pronto. Contáctanos para renovarla", + "SubscriptionExpired": "Tu suscripción a Backup Manager ha expirado. Contáctanos para renovarla", + "LanguageChange": "Algunos cambios tendrán efecto después de reiniciar la aplicación", + "MissingDataLoginError": "Por favor completa todos los campos obligatorios", + "WrongEmailLoginError": "La dirección de correo electrónico proporcionada no es válida. Introduce una correcta", + "LoginOk": "¡Bienvenido! Has iniciado sesión correctamente", + "ErrorTextClipboard": "El texto de error se ha copiado al portapapeles", + "CsvExport": "¡Copias de seguridad exportadas a CSV con éxito!", + "CsvExportError": "Error al exportar las copias de seguridad a CSV", + "CsvExportInvalidFilename": "Nombre de archivo no válido. Usa solo caracteres alfanuméricos, guiones y guiones bajos", + "NotSupportedEmail": "Tu sistema no admite el envío directo de correos electrónicos desde esta aplicación", + "UnableToSendEmail": "No se pudo enviar el correo. Inténtalo más tarde", + "NotSupportedEmailGeneric": "Tu sistema no admite el envío de correos electrónicos", + "OpeningWebsiteError": "No se pudo abrir la página web. Inténtalo de nuevo", + "FolderNotExisting": "La carpeta no existe o no es válida", + "BackupNameAlreadyUsed": "¡Nombre de copia de seguridad ya utilizado!", + "BackupAlreadyInProgress": "Ya hay una copia de seguridad en progreso. No es posible ejecutar copias en paralelo", + "BackupPathsEmptyError": "The initial path and destination path cannot be empty!" + } } diff --git a/src/main/resources/res/languages/fra.json b/src/main/resources/res/languages/fra.json index 69b3545f..53061f8d 100644 --- a/src/main/resources/res/languages/fra.json +++ b/src/main/resources/res/languages/fra.json @@ -1,208 +1,267 @@ { - "General": { - "AppName": "Backup Manager", - "Backup": "Sauvegarde", - "Version": "Version", - "From": "De", - "To": "À", - "OkButton": "OK", - "CancelButton": "Annuler", - "CloseButton": "Fermer", - "ApplyButton": "Appliquer", - "SaveButton": "Enregistrer", - "CreateButton": "Créer" - }, - "Menu": { - "File": "Fichier", - "Options": "Options", - "About": "À propos", - "Help": "Aide", - "BugReport": "Signaler un bug", - "Clear": "Effacer", - "Donate": "Faire un don", - "History": "Historique", - "InfoPage": "Info", - "New": "Nouveau", - "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" - }, - "TabbedFrames": { - "BackupEntry": "Entrée de Sauvegarde", - "BackupList": "Liste de Sauvegardes" - }, - "BackupEntry": { - "PageTitle": "Entrée de Sauvegarde", - "CurrentFile": "Fichier actuel", - "Notes": "Notes", - "LastBackup": "Dernière sauvegarde", - "SingleBackupButton": "Sauvegarde Unique", - "AutoBackupButton": "Sauvegarde Automatique", - "AutoBackupButtonON": "Sauvegarde Automatique (ACTIVÉE)", - "AutoBackupButtonOFF": "Sauvegarde Automatique (DÉSACTIVÉE)", - "InitialPathPlaceholder": "Chemin initial", - "DestinationPathPlaceholder": "Chemin de destination", - "BackupName": "Nom de la sauvegarde", - "BackupNameTooltip": "(Requis) Nom de la sauvegarde", - "InitialPathTooltip": "(Requis) Chemin initial", - "DestinationPathTooltip": "(Requis) Chemin de destination", - "InitialFileChooserTooltip": "Ouvrir l'explorateur de fichiers", - "DestinationFileChooserTooltip": "Ouvrir l'explorateur de fichiers", - "NotesTooltip": "(Optionnel) Description de la sauvegarde", - "SingleBackupTooltip": "Effectuer la sauvegarde", - "AutoBackupTooltip": "Activer/Désactiver la sauvegarde automatique", - "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", - "InitialPathColumn": "Chemin Initial", - "DestinationPathColumn": "Chemin de Destination", - "LastBackupColumn": "Dernière Sauvegarde", - "AutomaticBackupColumn": "Sauvegarde Automatique", - "NextBackupDateColumn": "Prochaine Date de Sauvegarde", - "TimeIntervalColumn": "Intervalle de Temps", - "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", - "ExportAs": "Exporter en tant que : ", - "ExportAsPdfTooltip": "Exporter en tant que PDF", - "ExportAsCsvTooltip": "Exporter en tant que CSV", - "ResearchBarTooltip": "Barre de recherche", - "ResearchBarPlaceholder": "Rechercher...", - "EditPopup": "Modifier", - "DeletePopup": "Supprimer", - "InterruptPopup": "Interrompre", - "DuplicatePopup": "Dupliquer", - "RenameBackupPopup": "Renommer la sauvegarde", - "OpenInitialFolderPopup": "Ouvrir le chemin initial", - "OpenDestinationFolderPopup": "Ouvrir le chemin de destination", - "BackupPopup": "Sauvegarde", - "SingleBackupPopup": "Effectuer une sauvegarde unique", - "AutoBackupPopup": "Sauvegarde automatique", - "CopyTextPopup": "Copier le texte", - "CopyBackupNamePopup": "Copier le nom de la sauvegarde", - "CopyInitialPathPopup": "Copier le chemin initial", - "CopyDestinationPathPopup": "Copier le chemin de destination" - }, - "TimePickerDialog": { - "TimeIntervalTitle": "Intervalle de temps pour les sauvegardes automatiques", - "Description": "Sélectionnez la fréquence des sauvegardes automatiques en \nchoisissant le nombre de jours, d'heures et de minutes.", - "Days": "Jours", - "Hours": "Heures", - "Minutes": "Minutes", - "SpinnerTooltip": "Roulette de la souris pour ajuster la valeur" - }, - "PreferencesDialog": { - "PreferencesTitle": "Préférences", - "Language": "Langue", - "Theme": "Thème" - }, - "UserDialog": { - "UserTitle": "Entrez vos informations", - "Name": "Prénom", - "Surname": "Nom de famille", - "Email": "E-Mail", - "ErrorMessageForMissingData": "Veuillez remplir tous les champs requis.", - "ErrorMessageForWrongEmail": "L'adresse e-mail fournie n'est pas valide. Veuillez en fournir une correcte.", - "EmailConfirmationSubject": "Merci d'avoir choisi Backup Manager !", - "EmailConfirmationBody": "Bonjour [UserName],\n\nMerci d'avoir téléchargé et enregistré Backup Manager, votre nouvel outil pour une gestion sécurisée et efficace des sauvegardes !\n\nCeci est un email automatique envoyé pour confirmer votre inscription. Nous vous contacterons uniquement par email pour vous informer des nouvelles versions ou des mises à jour importantes de l'application.\n\nEn attendant, si vous avez des questions, besoin d'aide ou des suggestions, nous sommes toujours à votre disposition. Vous pouvez nous contacter à [SupportEmail].\n\nMerci encore d'avoir choisi Backup Manager et bonne gestion de vos sauvegardes !\n\nCordialement,\nL'équipe de Backup Manager" - }, - "ProgressBackupFrame": { - "ProgressBackupTitle": "Sauvegarde en cours", - "StatusCompleted": "Sauvegarde terminée !", - "StatusLoading": "Chargement..." - }, - "TrayIcon": { - "TrayTooltip": "Service de Sauvegardes", - "OpenAction": "Accès Rapide", - "ExitAction": "Quitter", - "SuccessMessage": "\nLa sauvegarde a été effectuée avec succès :", - "ErrorMessageInputMissing": "\nErreur lors de la sauvegarde automatique.\nEntrée manquante !", - "ErrorMessageFilesNotExisting": "\nErreur lors de la sauvegarde automatique.\nUn ou les deux chemins n'existent pas !", - "ErrorMessageSamePaths": "\nErreur lors de la sauvegarde automatique.\nLe chemin initial et le chemin de destination ne peuvent pas être identiques. Veuillez choisir des chemins différents !" - }, - "Dialogs": { - "ErrorGenericTitle": "Erreur", - "WarningGenericTitle": "Avertissement", - "WarningBackupAlreadyInProgressMessage": "Une sauvegarde est déjà en cours. Il n'est pas possible d'effectuer des sauvegardes en parallèle.", - "WarningShortTimeIntervalMessage": "L'intervalle de temps sélectionné est très court. Pour un fonctionnement optimal, nous recommandons de le régler à au moins une heure. Voulez-vous quand même continuer ?", - "ErrorMessageForFolderNotExisting": "Le dossier n'existe pas ou est invalide", - "ErrorMessageForSavingFileWithPathsEmpty": "Impossible d'enregistrer le fichier. Les chemins initial et de destination doivent être spécifiés et ne peuvent pas être vides", - "BackupSavedCorrectlyTitle": "Sauvegarde Enregistrée", - "BackupSavedCorrectlyMessage": "enregistrée avec succès.", - "ErrorSavingBackupMessage": "Erreur lors de l'enregistrement de la sauvegarde", - "BackupNameInput": "Nom de la sauvegarde", - "ConfirmationRequiredTitle": "Confirmation requise", - "DuplicatedBackupNameMessage": "Une sauvegarde avec le même nom existe déjà. Voulez-vous l'écraser ?", - "BackupNameAlreadyUsedMessage": "Nom de sauvegarde déjà utilisé !", - "ErrorMessageForIncorrectInitialPath": "Erreur lors de l'opération de sauvegarde : le chemin initial est incorrect !", - "ExceptionMessageTitle": "Erreur...", - "ExceptionMessageClipboardMessage": "Le texte de l'erreur a été copié dans le presse-papiers.", - "ExceptionMessageClipboardButton": "Copier dans le presse-papiers", - "ExceptionMessageReportButton": "Signaler le problème", - "ExceptionMessageReportMessage": "Veuillez signaler cette erreur, soit avec une capture d'écran, soit en copiant le texte d'erreur suivant (il est recommandé de fournir une description des actions effectuées avant l'erreur) :", - "ErrorMessageOpeningWebsite": "Échec de l'ouverture de la page web. Veuillez réessayer.", - "ConfirmationMessageForClear": "Êtes-vous sûr de vouloir effacer les champs ?", - "ConfirmationMessageForUnsavedChanges": "Des modifications non enregistrées existent. Voulez-vous les enregistrer avant de passer à une autre sauvegarde ?", - "ErrorMessageOpenHistoryFile": "Erreur lors de l'ouverture du fichier d'historique.", - "ConfirmationMessageBeforeDeleteBackup": "Êtes-vous sûr de vouloir supprimer cet élément ? Cette action est irréversible.", - "ShareLinkCopiedMessage": "Lien de partage copié dans le presse-papiers !", - "ConfirmationMessageCancelAutoBackup": "Êtes-vous sûr de vouloir annuler les sauvegardes automatiques pour cette entrée ?", - "ErrorMessageUnableToSendEmail": "Impossible d'envoyer l'e-mail. Veuillez réessayer plus tard.", - "ErrorMessageNotSupportedEmail": "Votre système ne prend pas en charge l'envoi d'e-mails directement depuis cette application.", - "ErrorMessageNotSupportedEmailGeneric": "Votre système ne prend pas en charge l'envoi d'e-mails.", - "ErrorWrongTimeInterval": "L'intervalle de temps est incorrect", - "AutoBackupActivatedMessage": "La sauvegarde automatique a été activée", - "SettedEveryMessage": "\nEst défini tous les", - "DaysMessage": " jours", - "InterruptBackupProcessMessage": "Êtes-vous sûr de vouloir arrêter cette sauvegarde ?", - "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 !", - "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.", - "ErrorMessageCountingFiles": "Erreur lors du calcul des fichiers à sauvegarder.", - "ErrorMessageZippingGeneric": "Erreur lors de la compression des fichiers.", - "ErrorMessageZippingIO": "Erreur lors de la compression des fichiers : erreur d'E/S.", - "ErrorMessageZippingSecurity": "Erreur lors de la compression des fichiers : Erreur de sécurité.", - "SuccessGenericTitle": "Succès", - "SuccessfullyExportedToCsvMessage": "Sauvegardes exportées en CSV avec succès !", - "SuccessfullyExportedToPdfMessage": "Sauvegardes exportées en PDF avec succès !", - "ErrorMessageForExportingToCsv": "Erreur lors de l'exportation des sauvegardes en CSV : ", - "ErrorMessageForExportingToPdf": "Erreur lors de l'exportation des sauvegardes en PDF : ", - "CsvNameMessageInput": "Entrez le nom du fichier CSV.", - "PdfNameMessageInput": "Entrez le nom du fichier PDF.", - "DuplicatedFileNameMessage": "Le fichier existe déjà. Écraser?", - "ErrorMessageInvalidFilename": "Nom de fichier invalide. Utilisez uniquement des caractères alphanumériques, des tirets et des underscores.", - "ConfirmationDeletionTitle": "Confirmer la suppression", - "ConfirmationDeletionMessage": "Êtes-vous sûr de vouloir supprimer les lignes sélectionnées ?" - }, - "Subscription": { - "ExpiringTitle": "L'abonnement Backup Manager expire bientôt", - "ExpiringMessage": "Votre abonnement Backup Manager est sur le point d'expirer.\nLes sauvegardes automatiques continueront de fonctionner jusqu'à la date d'expiration.\nVeuillez contacter le support pour le renouveler.", - "ExpiredTitle": "L'abonnement Backup Manager a expiré", - "ExpiredMessage": "Votre abonnement Backup Manager a expiré.\nLes sauvegardes automatiques ne seront plus exécutées.\nVeuillez contacter le support pour le réactiver." - } + "General": { + "AppName": "Backup Manager", + "CancelButton": "Annuler", + "Backup": "Sauvegarde", + "Version": "Version", + "ApplyButton": "Appliquer", + "OkButton": "OK", + "From": "De", + "SaveButton": "Enregistrer", + "CloseButton": "Fermer", + "CreateButton": "Créer", + "EditButton": "Modifier", + "DeleteButton": "Supprimer", + "QuickSearch": "Recherche rapide", + "ContactUs": "Nous contacter", + "To": "À" + }, + "TrayIcon": { + "SuccessMessage": "\nLa sauvegarde a été effectuée avec succès :", + "ExitAction": "Quitter", + "ErrorMessageFilesNotExisting": "\nErreur lors de la sauvegarde automatique.\nUn ou les deux chemins n'existent pas !", + "TrayTooltip": "Service de Sauvegardes", + "OpenAction": "Accès Rapide", + "ErrorMessageInputMissing": "\nErreur lors de la sauvegarde automatique.\nEntrée manquante !", + "ErrorMessageSamePaths": "\nErreur lors de la sauvegarde automatique.\nLe chemin initial et le chemin de destination ne peuvent pas être identiques. Veuillez choisir des chemins différents !" + }, + "Dialogs": { + "WarningGenericTitle": "Avertissement", + "ErrorMessageForPathNotExisting": "Un ou les deux chemins n'existent pas !", + "ExceptionMessageReportMessage": "Veuillez signaler cette erreur, soit avec une capture d'écran, soit en copiant le texte d'erreur suivant (il est recommandé de fournir une description des actions effectuées avant l'erreur) :", + "ErrorGenericTitle": "Erreur", + "ErrorMessageForSamePaths": "Le chemin initial et le chemin de destination ne peuvent pas être identiques. Veuillez choisir des chemins différents !", + "SettedEveryMessage": "\nEst défini tous les", + "WarningBackupAlreadyInProgressMessage": "Une sauvegarde est déjà en cours. Il n'est pas possible d'effectuer des sauvegardes en parallèle.", + "WarningShortTimeIntervalMessage": "L'intervalle de temps sélectionné est très court. Pour un fonctionnement optimal, nous recommandons de le régler à au moins une heure. Voulez-vous quand même continuer ?", + "ConfirmationMessageCancelAutoBackup": "Êtes-vous sûr de vouloir annuler les sauvegardes automatiques pour cette entrée ?", + "ErrorMessageCountingFiles": "Erreur lors du calcul des fichiers à sauvegarder.", + "ErrorMessageInputMissingGeneric": "Entrée manquante !", + "BackupNameInput": "Nom de la sauvegarde", + "CsvNameMessageInput": "Entrez le nom du fichier CSV.", + "ErrorMessageZippingGeneric": "Erreur lors de la compression des fichiers.", + "ErrorMessageZippingIO": "Erreur lors de la compression des fichiers : erreur d'E/S.", + "DuplicatedFileNameMessage": "Le fichier existe déjà. Écraser?", + "AutoBackupActivatedMessage": "La sauvegarde automatique a été activée", + "AutoBackup": "Sauvegarde automatique", + "DaysMessage": " jours", + "ExceptionMessageReportButton": "Signaler le problème", + "SuccessGenericTitle": "Succès", + "DuplicatedBackupNameMessage": "Une sauvegarde avec le même nom existe déjà. Voulez-vous l'écraser ?", + "ExceptionMessageClipboardButton": "Copier dans le presse-papiers", + "ErrorMessageZippingSecurity": "Erreur lors de la compression des fichiers : Erreur de sécurité.", + "InterruptBackupProcessMessage": "Êtes-vous sûr de vouloir arrêter cette sauvegarde ?", + "ConfirmationMessageBeforeDeleteBackup": "Êtes-vous sûr de vouloir supprimer cet élément ? Cette action est irréversible.", + "ConfirmationRequiredTitle": "Confirmation requise" + }, + "TimePickerDialog": { + "TimeIntervalTitle": "Intervalle de temps pour les sauvegardes automatiques", + "Days": "Jours", + "Hours": "Heures", + "SpinnerTooltip": "Roulette de la souris pour ajuster la valeur", + "Description": "Sélectionnez la fréquence des sauvegardes automatiques en \nchoisissant le nombre de jours, d'heures et de minutes.", + "Minutes": "Minutes" + }, + "Subscription": { + "ExpiredTitle": "L'abonnement Backup Manager a expiré", + "ExpiringMessage": "Votre abonnement Backup Manager est sur le point d'expirer.\nLes sauvegardes automatiques continueront de fonctionner jusqu'à la date d'expiration.\nVeuillez contacter le support pour le renouveler.", + "ExpiringTitle": "L'abonnement Backup Manager expire bientôt", + "ExpiredMessage": "Votre abonnement Backup Manager a expiré.\nLes sauvegardes automatiques ne seront plus exécutées.\nVeuillez contacter le support pour le réactiver.", + "ActiveLabel": "Actif", + "ExpiringLabel": "Bientôt expiré", + "ExpiredLabel": "Expiré", + "Status": "Statut de l'abonnement", + "ValidFrom": "Valide à partir du", + "ValidTo": "Valide jusqu'au", + "ToExtend": "pour prolonger la période d'abonnement." + }, + "ProgressBackupFrame": { + "StatusLoading": "Chargement...", + "ProgressBackupTitle": "Sauvegarde en cours", + "StatusCompleted": "Sauvegarde terminée !" + }, + "Menu": { + "About": "À propos", + "File": "Fichier", + "BugReport": "Signaler un bug", + "Options": "Options", + "New": "Nouveau", + "Help": "Aide", + "History": "Historique", + "SubmenuMain": "PRINCIPAL", + "SubmenuOther": "AUTRE", + "Donate": "Soutenir le projet", + "Backups": "Liste des sauvegardes", + "ImportBackup": "Importer des sauvegardes depuis CSV", + "ExportBackup": "Exporter les sauvegardes vers CSV", + "Dashboard": "Tableau de bord", + "Github": "Page GitHub", + "Paypal": "PayPal", + "BuyMeACoffe": "Offrez-moi un café", + "Subscription": "Abonnement" + }, + "BackupList": { + "NotesDetail": "Notes", + "BackupNameDetail": "NomSauvegarde", + "EditPopup": "Modifier", + "ResearchBarTooltip": "Barre de recherche", + "InitialPathDetail": "CheminInitial", + "RenameBackupPopup": "Renommer la sauvegarde", + "NextBackupDateDetail": "ProchaineSauvegarde", + "AutoBackupPopup": "Sauvegarde automatique", + "BackupNameColumn": "Nom de la Sauvegarde", + "BackupPopup": "Sauvegarde", + "BackupCountDetail": "NombreSauvegardes", + "InitialPathColumn": "Chemin Initial", + "MaxBackupsToKeepDetail": "NombreMaxSauvegardesConserver", + "DeletePopup": "Supprimer", + "OpenDestinationFolderPopup": "Ouvrir le chemin de destination", + "LastBackupColumn": "Dernière Sauvegarde", + "SingleBackupPopup": "Effectuer une sauvegarde unique", + "CopyTextPopup": "Copier le texte", + "DestinationPathDetail": "CheminDestination", + "CopyDestinationPathPopup": "Copier le chemin de destination", + "LastBackupDetail": "DernièreSauvegarde", + "OpenInitialFolderPopup": "Ouvrir le chemin initial", + "ResearchBarPlaceholder": "Rechercher...", + "InterruptPopup": "Interrompre", + "DuplicatePopup": "Dupliquer", + "NextBackupDateColumn": "Prochaine Date de Sauvegarde", + "DestinationPathColumn": "Chemin de Destination", + "AutomaticBackupColumn": "Sauvegarde Automatique", + "LastUpdateDateDetail": "DernièreMiseJour", + "TimeIntervalDetail": "IntervalleTemps", + "CopyBackupNamePopup": "Copier le nom de la sauvegarde", + "CreationDateDetail": "DateCréation", + "CopyInitialPathPopup": "Copier le chemin initial", + "BackupListTitle": "Liste des sauvegardes", + "BackupListDescription": "Gérez et surveillez les configurations de sauvegarde, y compris la création, la modification, la planification et l’exécution.", + "TimeIntervalColumn": "Intervalle (jj.HH:mm)", + "MaxBackupsColumn": "Nombre maximal de sauvegardes à conserver" + }, + "UserDialog": { + "EmailConfirmationBody": "Bonjour [UserName],\n\nMerci d'avoir téléchargé et enregistré Backup Manager, votre nouvel outil pour une gestion sécurisée et efficace des sauvegardes !\n\nCeci est un email automatique envoyé pour confirmer votre inscription. Nous vous contacterons uniquement par email pour vous informer des nouvelles versions ou des mises à jour importantes de l'application.\n\nEn attendant, si vous avez des questions, besoin d'aide ou des suggestions, nous sommes toujours à votre disposition. Vous pouvez nous contacter à [SupportEmail].\n\nMerci encore d'avoir choisi Backup Manager et bonne gestion de vos sauvegardes !\n\nCordialement,\nL'équipe de Backup Manager", + "EmailConfirmationSubject": "Merci d'avoir choisi Backup Manager !", + "Surname": "Nom de famille", + "Name": "Prénom", + "Email": "E-Mail", + "UserTitle": "Entrez vos informations", + "UserDescription": "Veuillez saisir vos informations pour accéder au système", + "UserNamePlaceholder": "Entrez votre prénom", + "UserSurnamePlaceholder": "Entrez votre nom de famille", + "UserEmailPlaceholder": "Entrez votre adresse e-mail" + }, + "BackupEntry": { + "TimePickerTooltip": "Sélecteur de temps", + "MaxBackupsToKeepTooltip": "Nombre maximum de sauvegardes avant de supprimer les plus anciennes.", + "BackupName": "Nom de la sauvegarde", + "InitialPathTooltip": "(Requis) Chemin initial", + "InitialFileChooserTooltip": "Ouvrir l'explorateur de fichiers", + "LastBackup": "Dernière sauvegarde", + "SingleBackupButton": "Sauvegarde Unique", + "AutoBackupButtonON": "Sauvegarde Automatique (ACTIVÉE)", + "AutoBackupButtonOFF": "Sauvegarde Automatique (DÉSACTIVÉE)", + "DestinationPathTooltip": "(Requis) Chemin de destination", + "MaxBackupsToKeep": "Nombre maximum de sauvegardes à conserver", + "SingleBackupTooltip": "Effectuer la sauvegarde", + "AutoBackupTooltip": "Activer/Désactiver la sauvegarde automatique", + "NotesTooltip": "(Optionnel) Description de la sauvegarde", + "DestinationFileChooserTooltip": "Ouvrir l'explorateur de fichiers", + "BackupNameTooltip": "(Requis) Nom de la sauvegarde", + "Notes": "Notes", + "PageSubtitleCreate": "Créer une sauvegarde", + "PageSubtitleEdit": "Modifier la sauvegarde", + "PageSubtitleInfo": "Informations sur la sauvegarde", + "PageSubtitleSettings": "Paramètres des sauvegardes", + "Paths": "Chemins", + "BackupNamePlaceholder": "Nom de la sauvegarde (unique)", + "InitialPathPlaceholder": "Chemin source ex. C:\\Users\\Admin\\Documents", + "DestinationPathPlaceholder": "Dossier de destination ex. D:\\Backups" + }, + "HistoryLogs": { + "HistoryLogsTitle": "Journaux d'historique", + "HistoryLogsDescription": "Vous trouverez ici les journaux de l'application, utiles pour le dépannage et pour comprendre le comportement de l'application au fil du temps." + }, + "About": { + "AboutSystemInformation": "Informations système", + "AboutMessageBody": "Backup Manager est une application simple et puissante conçue pour automatiser les sauvegardes de dossiers et sous-dossiers.

Les utilisateurs peuvent planifier des sauvegardes automatiques ou exécuter des sauvegardes manuelles à tout moment.

L’historique des sauvegardes est stocké en toute sécurité, offrant un contrôle total sur les données enregistrées.

Visitez le site du projet pour plus d’informations.

" + }, + "Dashboard": { + "DashboardTitle": "Tableau de bord analytique des sauvegardes", + "DashboardCardTotalConfigurations": "Configurations de sauvegarde totales", + "DashboardCardTotalExecutions": "Exécutions de sauvegarde totales", + "DashboardCardSuccessRate": "Taux de réussite", + "DashboardCardAvgDuration": "Durée moyenne des sauvegardes", + "DashboardCardCompressionRate": "Taux de compression", + "DashboardChartExecutions": "Exécutions des sauvegardes", + "DashboardChartAvgDuration": "Durée moyenne des sauvegardes (min)" + }, + "Settings": { + "SettingsLayoutTab": "Disposition", + "SettingsStyleTab": "Style", + "SettingsWindowsLayout": "Disposition des fenêtres", + "SettingsWindowsRight": "De droite à gauche", + "SettingsWindowsFull": "Contenu fenêtre complet", + "SettingsDrawerLayout": "Disposition du panneau", + "SettingsDrawerLeft": "Gauche", + "SettingsDrawerLeading": "Début", + "SettingsDrawerTrailing": "Fin", + "SettingsDrawerRight": "Droite", + "SettingsDrawerTop": "Haut", + "SettingsDrawerBottom": "Bas", + "SettingsModalOption": "Option modale par défaut", + "SettingsModalAnimation": "Activer l’animation", + "SettingsModalClose": "Fermer avec la touche Échap", + "SettingsLanguagesLayout": "Langue", + "SettingsAccentLayout": "Couleur d’accent", + "SettingsColorPickerLayout": "Sélecteur de couleur", + "SettingsDrawerLineLayout": "Style de ligne du panneau", + "SettingsDrawerLineCurved": "Ligne courbe", + "SettingsDrawerDotLine": "Ligne pointillée droite", + "SettingsLineStyleLayout": "Option style de ligne", + "SettingsLineStyleRettangle": "Rectangle", + "SettingsLineStyleEllipse": "Ellipse", + "SettingsLineStyleLine": "Ligne", + "SettingsLineStyleCurved": "Courbe", + "SettingsColorOptionLayout": "Option de couleur", + "SettingsColorOptionPainted": "Colorer la ligne sélectionnée", + "SettingsThemes": "Thèmes", + "SettingsThemeAll": "Tous", + "SettingsThemeLight": "Clair", + "SettingsThemeDark": "Sombre" + }, + "SearchBar": { + "SearcTitle": "Rechercher...", + "SearchNoRecent": "Aucune recherche récente", + "SearchNoResult": "Aucun résultat pour", + "SearchFavorite": "Favori", + "SearchRecent": "Récents" + }, + "Toast": { + "BackupEditedOk": "Sauvegarde mise à jour avec succès", + "BackupCreatedOk": "Sauvegarde créée avec succès", + "BackupDeletedOk": "Sauvegarde supprimée avec succès", + "BackupReplacedOk": "Sauvegarde existante remplacée avec succès", + "BackupDeletedError": "Échec de la suppression de la sauvegarde", + "BackupReplacedError": "Échec du remplacement de la sauvegarde", + "InvalidTime": "Intervalle de temps invalide", + "SubscriptionExpiring": "Votre abonnement Backup Manager expirera bientôt. Veuillez nous contacter pour le renouveler", + "SubscriptionExpired": "Votre abonnement Backup Manager a expiré. Veuillez nous contacter pour le renouveler", + "LanguageChange": "Certaines modifications prendront effet après le redémarrage de l'application", + "MissingDataLoginError": "Veuillez remplir tous les champs obligatoires", + "WrongEmailLoginError": "L'adresse e-mail fournie est invalide. Veuillez en saisir une correcte", + "LoginOk": "Bienvenue ! Connexion réussie", + "ErrorTextClipboard": "Le texte d’erreur a été copié dans le presse-papiers", + "CsvExport": "Sauvegardes exportées en CSV avec succès !", + "CsvExportError": "Erreur lors de l’exportation des sauvegardes en CSV", + "CsvExportInvalidFilename": "Nom de fichier invalide. Utilisez uniquement des caractères alphanumériques, des tirets et des underscores", + "NotSupportedEmail": "Votre système ne prend pas en charge l’envoi d’e-mails directement depuis cette application", + "UnableToSendEmail": "Impossible d’envoyer l’e-mail. Veuillez réessayer plus tard", + "NotSupportedEmailGeneric": "Votre système ne prend pas en charge l’envoi d’e-mails", + "OpeningWebsiteError": "Échec de l’ouverture de la page web. Veuillez réessayer", + "FolderNotExisting": "Le dossier n’existe pas ou est invalide", + "BackupNameAlreadyUsed": "Nom de sauvegarde déjà utilisé !", + "BackupAlreadyInProgress": "Une sauvegarde est déjà en cours. Il n’est pas possible d’exécuter des sauvegardes en parallèle", + "BackupPathsEmptyError": "The initial path and destination path cannot be empty!" + } } diff --git a/src/main/resources/res/languages/ita.json b/src/main/resources/res/languages/ita.json index 430389b0..7238200a 100644 --- a/src/main/resources/res/languages/ita.json +++ b/src/main/resources/res/languages/ita.json @@ -1,208 +1,267 @@ { - "General": { - "AppName": "Backup Manager", - "Backup": "Backup", - "Version": "Versione", - "From": "Da", - "To": "A", - "CloseButton": "Chiudi", - "OkButton": "Ok", - "CancelButton": "Annulla", - "ApplyButton":"Applica", - "SaveButton": "Salva", - "CreateButton": "Crea" - }, - "Menu": { - "File": "File", - "Options": "Opzioni", - "About": "Informazioni", - "Help": "Aiuto", - "BugReport": "Segnala un problema", - "Clear": "Pulisci", - "Donate": "Donazione", - "History": "Storico", - "InfoPage": "Info", - "New": "Nuovo", - "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" - }, - "TabbedFrames": { - "BackupEntry": "VoceBackup", - "BackupList": "ListaBackup" - }, - "BackupEntry": { - "PageTitle": "Voce di Backup", - "CurrentFile": "File corrente", - "Notes": "Note", - "LastBackup": "Ultimo backup", - "SingleBackupButton": "Backup Singolo", - "AutoBackupButton": "Backup Automatico", - "AutoBackupButtonON": "Backup Automatico (ON)", - "AutoBackupButtonOFF": "Backup Automatico (OFF)", - "InitialPathPlaceholder": "Percorso iniziale", - "DestinationPathPlaceholder": "Percorso di destinazione", - "BackupName": "Nome del backup", - "BackupNameTooltip": "(Obbligatorio) Nome del backup", - "InitialPathTooltip": "(Obbligatorio) Percorso iniziale", - "DestinationPathTooltip": "(Obbligatorio) Percorso di destinazione", - "InitialFileChooserTooltip": "Apri esplora file", - "DestinationFileChooserTooltip": "Apri esplora file", - "NotesTooltip": "(Opzionale) Descrizione del backup", - "SingleBackupTooltip": "Esegui il backup", - "AutoBackupTooltip": "Attiva/Disattiva backup automatico", - "TimePickerTooltip": "Selettore orario", - "MaxBackupsToKeep": "Massimo numero di backup da mantenere", - "MaxBackupsToKeepTooltip": "Numero massimo di backup da conservare prima di eliminare i più vecchi." - }, - "BackupList": { - "BackupNameColumn": "Nome del Backup", - "InitialPathColumn": "Percorso Iniziale", - "DestinationPathColumn": "Percorso di Destinazione", - "LastBackupColumn": "Ultimo Backup", - "AutomaticBackupColumn": "Backup Automatico", - "NextBackupDateColumn": "Data del Prossimo Backup", - "TimeIntervalColumn": "Intervallo di Tempo", - "BackupNameDetail": "NomeBackup", - "InitialPathDetail": "PercorsoIniziale", - "DestinationPathDetail": "PercorsoDestinazione", - "LastBackupDetail": "UltimoBackup", - "NextBackupDateDetail": "ProssimoBackup", - "TimeIntervalDetail": "IntervalloDiTempo", - "CreationDateDetail": "DataCreazione", - "LastUpdateDateDetail": "DataUltimoAggiornamento", - "BackupCountDetail": "ConteggioBackup", - "MaxBackupsToKeepDetail": "MassimoNumeroBackupDaMantenere", - "NotesDetail": "Note", - "AddBackupTooltip": "Aggiungi nuovo backup", - "ExportAs": "Esporta come: ", - "ExportAsPdfTooltip": "Esporta come PDF", - "ExportAsCsvTooltip": "Esporta come CSV", - "ResearchBarTooltip": "Barra di ricerca", - "ResearchBarPlaceholder": "Cerca...", - "EditPopup": "Modifica", - "DeletePopup": "Elimina", - "InterruptPopup": "Interrompi", - "DuplicatePopup": "Duplica", - "RenameBackupPopup": "Rinomina backup", - "OpenInitialFolderPopup": "Apri percorso iniziale", - "OpenDestinationFolderPopup": "Apri percorso di destinazione", - "BackupPopup": "Backup", - "SingleBackupPopup": "Esegui backup singolo", - "AutoBackupPopup": "Backup automatico", - "CopyTextPopup": "Copia testo", - "CopyBackupNamePopup": "Copia nome backup", - "CopyInitialPathPopup": "Copia percorso iniziale", - "CopyDestinationPathPopup": "Copia percorso di destinazione" - }, - "TimePickerDialog": { - "TimeIntervalTitle": "Intervallo di tempo per backup automatico", - "Description": "Seleziona la frequenza del backup automatico \nscegliendo la frequenza in giorni, ore e minuti.", - "Days": "Giorni", - "Hours": "Ore", - "Minutes": "Minuti", - "SpinnerTooltip": "Usa la rotellina per regolare il valore" - }, - "PreferencesDialog": { - "PreferencesTitle": "Preferenze", - "Language": "Lingua", - "Theme": "Tema" - }, - "UserDialog": { - "UserTitle": "Inserisci i tuoi dati", - "Name": "Nome", - "Surname": "Cognome", - "Email": "Email", - "ErrorMessageForMissingData": "Per favore, compila tutti i campi richiesti.", - "ErrorMessageForWrongEmail": "L'indirizzo email fornito non è valido. Inseriscine uno corretto.", - "EmailConfirmationSubject": "Grazie per aver scelto Backup Manager!", - "EmailConfirmationBody": "Ciao [UserName],\n\nGrazie per aver scaricato e registrato Backup Manager, il tuo nuovo strumento per una gestione sicura ed efficiente dei backup!\n\nQuesta è un’email automatica, inviata per confermare la tua registrazione. Ti contatteremo via email esclusivamente per comunicarti il rilascio di nuove versioni o aggiornamenti importanti dell’applicazione.\n\nNel frattempo, se hai domande, necessiti assistenza o hai suggerimenti, siamo sempre a tua disposizione. Puoi contattarci scrivendo a [SupportEmail].\n\nGrazie ancora per aver scelto Backup Manager e buon lavoro con i tuoi backup!\n\nA presto,\nIl Team di Backup Manager" - }, - "ProgressBackupFrame": { - "ProgressBackupTitle": "Backup in corso", - "StatusCompleted": "Backup completato!", - "StatusLoading": "Caricamento..." - }, - "TrayIcon": { - "TrayTooltip": "Servizio di Backup", - "OpenAction": "Accesso Rapido", - "ExitAction": "Esci", - "SuccessMessage": "\nIl backup è stato completato con successo:", - "ErrorMessageInputMissing": "\nErrore durante il backup automatico.\nPercorso mancante!", - "ErrorMessageFilesNotExisting": "\nErrore durante il backup automatico.\nUno o entrambi i percorsi non esistono!", - "ErrorMessageSamePaths": "\nErrore durante il backup automatico.\nIl percorso iniziale e il percorso di destinazione non possono essere uguali. Scegli percorsi diversi!" - }, - "Dialogs": { - "ErrorGenericTitle": "Errore", - "WarningGenericTitle": "Avviso", - "WarningBackupAlreadyInProgressMessage": "È già in corso un backup. Non è possibile eseguire backup in parallelo.", - "WarningShortTimeIntervalMessage": "L'intervallo di tempo selezionato è molto breve. Per un funzionamento ottimale, consigliamo di impostarlo ad almeno un'ora. Vuoi comunque procedere?", - "ErrorMessageForFolderNotExisting": "La cartella non esiste o non è valida", - "ErrorMessageForSavingFileWithPathsEmpty": "Impossibile salvare il file. Entrambi i percorsi iniziale e di destinazione devono essere specificati e non possono essere vuoti", - "BackupSavedCorrectlyTitle": "Backup salvato", - "BackupSavedCorrectlyMessage": "salvato con successo!", - "ErrorSavingBackupMessage": "Errore durante il salvataggio del backup", - "BackupNameInput": "Nome del backup", - "ConfirmationRequiredTitle": "Conferma richiesta", - "DuplicatedBackupNameMessage": "Esiste già un backup con lo stesso nome, vuoi sovrascriverlo?", - "BackupNameAlreadyUsedMessage": "Nome del backup già utilizzato!", - "ErrorMessageForIncorrectInitialPath": "Errore durante il backup: il percorso iniziale è errato!", - "ExceptionMessageTitle": "Errore...", - "ExceptionMessageClipboardMessage": "Il testo dell'errore è stato copiato negli appunti.", - "ExceptionMessageClipboardButton":"Copia negli appunti", - "ExceptionMessageReportButton":"Riporta il problema", - "ExceptionMessageReportMessage": "Si prega di segnalare questo errore, allegando un'immagine dello schermo o copiando il seguente testo dell'errore (è apprezzato fornire una descrizione delle operazioni eseguite prima dell'errore):", - "ErrorMessageOpeningWebsite": "Impossibile aprire la pagina web. Riprova.", - "ConfirmationMessageForClear": "Sei sicuro di voler pulire i campi?", - "ConfirmationMessageForUnsavedChanges": "Ci sono modifiche non salvate, vuoi salvarle prima di passare a un altro backup?", - "ErrorMessageOpenHistoryFile": "Errore durante l'apertura del file storico.", - "ConfirmationMessageBeforeDeleteBackup": "Sei sicuro di voler eliminare questo elemento? Nota: questa azione non può essere annullata.", - "ShareLinkCopiedMessage": "Link di condivisione copiato negli appunti!", - "ConfirmationMessageCancelAutoBackup": "Sei sicuro di voler annullare il backup automatico?", - "ErrorMessageUnableToSendEmail": "Impossibile inviare l'email. Riprova più tardi.", - "ErrorMessageNotSupportedEmail": "Il tuo sistema non supporta l'invio di email direttamente da questa applicazione.", - "ErrorMessageNotSupportedEmailGeneric": "Il tuo sistema non supporta l'invio di email.", - "ErrorWrongTimeInterval":"L'intervallo di tempo non è corretto", - "AutoBackupActivatedMessage": "Backup Automatico attivato", - "SettedEveryMessage": "\nImpostato ogni", - "DaysMessage": " giorni", - "InterruptBackupProcessMessage": "Sei sicuro di voler interrompere questo backup?", - "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!", - "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: Selezionare un file JSON valido.", - "ErrorMessageCountingFiles": "Errore durante il calcolo dei file da eseguire il backup.", - "ErrorMessageZippingGeneric": "Errore durante la compressione dei file.", - "ErrorMessageZippingIO": "Errore durante la compressione dei file: errore di I/O.", - "ErrorMessageZippingSecurity": "Errore durante la compressione dei file: Errore di sicurezza.", - "SuccessGenericTitle": "Successo", - "SuccessfullyExportedToCsvMessage": "Backup esportati in CSV con successo!", - "SuccessfullyExportedToPdfMessage": "Backup esportati in PDF con successo!", - "ErrorMessageForExportingToCsv": "Errore durante l'esportazione dei backup in CSV: ", - "ErrorMessageForExportingToPdf": "Errore durante l'esportazione dei backup in PDF: ", - "CsvNameMessageInput": "Inserisci il nome del file CSV.", - "PdfNameMessageInput": "Inserisci il nome del file PDF.", - "DuplicatedFileNameMessage": "Il file esiste già. Sovrascrivere?", - "ErrorMessageInvalidFilename": "Nome file non valido. Usa solo caratteri alfanumerici, trattini e underscore.", - "ConfirmationDeletionTitle": "Conferma Eliminazione", - "ConfirmationDeletionMessage": "Sei sicuro di voler eliminare le righe selezionate?" - }, - "Subscription": { - "ExpiringTitle": "Abbonamento Backup Manager in scadenza", - "ExpiringMessage": "Il tuo abbonamento a Backup Manager sta per scadere.\nI backup automatici continueranno a funzionare fino alla data di scadenza.\nContatta l'assistenza per rinnovarlo.", - "ExpiredTitle": "Abbonamento Backup Manager scaduto", - "ExpiredMessage": "Il tuo abbonamento a Backup Manager è scaduto.\nI backup automatici non verranno più eseguiti.\nContatta l'assistenza per riattivarlo." - } + "General": { + "AppName": "Backup Manager", + "CancelButton": "Annulla", + "Backup": "Backup", + "Version": "Versione", + "ApplyButton": "Applica", + "OkButton": "Ok", + "From": "Da", + "SaveButton": "Salva", + "CloseButton": "Chiudi", + "CreateButton": "Crea", + "EditButton": "Modifica", + "DeleteButton": "Elimina", + "QuickSearch": "Ricerca rapida", + "ContactUs": "Contattaci", + "To": "A" + }, + "TrayIcon": { + "SuccessMessage": "\nIl backup è stato completato con successo:", + "ExitAction": "Esci", + "ErrorMessageFilesNotExisting": "\nErrore durante il backup automatico.\nUno o entrambi i percorsi non esistono!", + "TrayTooltip": "Servizio di Backup", + "OpenAction": "Accesso Rapido", + "ErrorMessageInputMissing": "\nErrore durante il backup automatico.\nPercorso mancante!", + "ErrorMessageSamePaths": "\nErrore durante il backup automatico.\nIl percorso iniziale e il percorso di destinazione non possono essere uguali. Scegli percorsi diversi!" + }, + "Dialogs": { + "WarningGenericTitle": "Avviso", + "ErrorMessageForPathNotExisting": "Uno o entrambi i percorsi non esistono!", + "ExceptionMessageReportMessage": "Si prega di segnalare questo errore, allegando un'immagine dello schermo o copiando il seguente testo dell'errore (è apprezzato fornire una descrizione delle operazioni eseguite prima dell'errore):", + "ErrorGenericTitle": "Errore", + "ErrorMessageForSamePaths": "Il percorso iniziale e il percorso di destinazione non possono essere identici. Scegli percorsi diversi!", + "SettedEveryMessage": "\nImpostato ogni", + "WarningBackupAlreadyInProgressMessage": "È già in corso un backup. Non è possibile eseguire backup in parallelo.", + "WarningShortTimeIntervalMessage": "L'intervallo di tempo selezionato è molto breve. Per un funzionamento ottimale, consigliamo di impostarlo ad almeno un'ora. Vuoi comunque procedere?", + "ConfirmationMessageCancelAutoBackup": "Sei sicuro di voler annullare il backup automatico?", + "ErrorMessageCountingFiles": "Errore durante il calcolo dei file da eseguire il backup.", + "ErrorMessageInputMissingGeneric": "Input Mancanti!", + "BackupNameInput": "Nome del backup", + "CsvNameMessageInput": "Inserisci il nome del file CSV.", + "ErrorMessageZippingGeneric": "Errore durante la compressione dei file.", + "ErrorMessageZippingIO": "Errore durante la compressione dei file: errore di I/O.", + "DuplicatedFileNameMessage": "Il file esiste già. Sovrascrivere?", + "AutoBackupActivatedMessage": "Backup Automatico attivato", + "AutoBackup": "Backup automatico", + "DaysMessage": " giorni", + "ExceptionMessageReportButton": "Riporta il problema", + "SuccessGenericTitle": "Successo", + "DuplicatedBackupNameMessage": "Esiste già un backup con lo stesso nome, vuoi sovrascriverlo?", + "ExceptionMessageClipboardButton": "Copia negli appunti", + "ErrorMessageZippingSecurity": "Errore durante la compressione dei file: Errore di sicurezza.", + "InterruptBackupProcessMessage": "Sei sicuro di voler interrompere questo backup?", + "ConfirmationMessageBeforeDeleteBackup": "Sei sicuro di voler eliminare questo elemento? Nota: questa azione non può essere annullata.", + "ConfirmationRequiredTitle": "Conferma richiesta" + }, + "TimePickerDialog": { + "TimeIntervalTitle": "Intervallo di tempo per backup automatico", + "Days": "Giorni", + "Hours": "Ore", + "SpinnerTooltip": "Usa la rotellina per regolare il valore", + "Description": "Seleziona la frequenza del backup automatico \nscegliendo la frequenza in giorni, ore e minuti.", + "Minutes": "Minuti" + }, + "Subscription": { + "ExpiredTitle": "Abbonamento Backup Manager scaduto", + "ExpiringMessage": "Il tuo abbonamento a Backup Manager sta per scadere.\nI backup automatici continueranno a funzionare fino alla data di scadenza.\nContatta l'assistenza per rinnovarlo.", + "ExpiringTitle": "Abbonamento Backup Manager in scadenza", + "ExpiredMessage": "Il tuo abbonamento a Backup Manager è scaduto.\nI backup automatici non verranno più eseguiti.\nContatta l'assistenza per riattivarlo.", + "ActiveLabel": "Attivo", + "ExpiringLabel": "In scadenza", + "ExpiredLabel": "Scaduto", + "Status": "Stato abbonamento", + "ValidFrom": "Valido dal", + "ValidTo": "Valido fino al", + "ToExtend": "per estendere il periodo di abbonamento." + }, + "ProgressBackupFrame": { + "StatusLoading": "Caricamento...", + "ProgressBackupTitle": "Backup in corso", + "StatusCompleted": "Backup completato!" + }, + "Menu": { + "About": "Informazioni", + "File": "File", + "BugReport": "Segnala un problema", + "Options": "Opzioni", + "New": "Nuovo", + "Help": "Aiuto", + "History": "Cronologia", + "SubmenuMain": "PRINCIPALE", + "SubmenuOther": "ALTRO", + "Donate": "Supporta il progetto", + "Backups": "Elenco backup", + "ImportBackup": "Importa backup da CSV", + "ExportBackup": "Esporta backup in CSV", + "Dashboard": "Dashboard", + "Github": "Pagina GitHub", + "Paypal": "PayPal", + "BuyMeACoffe": "Offrimi un caffè", + "Subscription": "Abbonamento" + }, + "BackupList": { + "NotesDetail": "Note", + "BackupNameDetail": "NomeBackup", + "EditPopup": "Modifica", + "ResearchBarTooltip": "Barra di ricerca", + "InitialPathDetail": "PercorsoIniziale", + "RenameBackupPopup": "Rinomina backup", + "NextBackupDateDetail": "ProssimoBackup", + "AutoBackupPopup": "Backup automatico", + "BackupNameColumn": "Nome del Backup", + "BackupPopup": "Backup", + "BackupCountDetail": "ConteggioBackup", + "InitialPathColumn": "Percorso Iniziale", + "MaxBackupsToKeepDetail": "MassimoNumeroBackupDaMantenere", + "DeletePopup": "Elimina", + "OpenDestinationFolderPopup": "Apri percorso di destinazione", + "LastBackupColumn": "Ultimo Backup", + "SingleBackupPopup": "Esegui backup singolo", + "CopyTextPopup": "Copia testo", + "DestinationPathDetail": "PercorsoDestinazione", + "CopyDestinationPathPopup": "Copia percorso di destinazione", + "LastBackupDetail": "UltimoBackup", + "OpenInitialFolderPopup": "Apri percorso iniziale", + "ResearchBarPlaceholder": "Cerca...", + "InterruptPopup": "Interrompi", + "DuplicatePopup": "Duplica", + "NextBackupDateColumn": "Data del Prossimo Backup", + "DestinationPathColumn": "Percorso di Destinazione", + "AutomaticBackupColumn": "Backup Automatico", + "LastUpdateDateDetail": "DataUltimoAggiornamento", + "TimeIntervalDetail": "IntervalloDiTempo", + "CopyBackupNamePopup": "Copia nome backup", + "CreationDateDetail": "DataCreazione", + "CopyInitialPathPopup": "Copia percorso iniziale", + "BackupListTitle": "Elenco backup", + "BackupListDescription": "Gestisci e monitora le configurazioni di backup, inclusa la creazione, modifica, pianificazione ed esecuzione.", + "TimeIntervalColumn": "Intervallo (gg.HH:mm)", + "MaxBackupsColumn": "Numero massimo di backup da mantenere" + }, + "UserDialog": { + "EmailConfirmationBody": "Ciao [UserName],\n\nGrazie per aver scaricato e registrato Backup Manager, il tuo nuovo strumento per una gestione sicura ed efficiente dei backup!\n\nQuesta è un’email automatica, inviata per confermare la tua registrazione. Ti contatteremo via email esclusivamente per comunicarti il rilascio di nuove versioni o aggiornamenti importanti dell’applicazione.\n\nNel frattempo, se hai domande, necessiti assistenza o hai suggerimenti, siamo sempre a tua disposizione. Puoi contattarci scrivendo a [SupportEmail].\n\nGrazie ancora per aver scelto Backup Manager e buon lavoro con i tuoi backup!\n\nA presto,\nIl Team di Backup Manager", + "EmailConfirmationSubject": "Grazie per aver scelto Backup Manager!", + "Surname": "Cognome", + "Name": "Nome", + "Email": "Email", + "UserTitle": "Inserisci i tuoi dati", + "UserDescription": "Inserisci i tuoi dati per accedere al sistema", + "UserNamePlaceholder": "Inserisci il tuo nome", + "UserSurnamePlaceholder": "Inserisci il tuo cognome", + "UserEmailPlaceholder": "Inserisci la tua email" + }, + "BackupEntry": { + "TimePickerTooltip": "Selettore orario", + "MaxBackupsToKeepTooltip": "Numero massimo di backup da conservare prima di eliminare i più vecchi.", + "BackupName": "Nome del backup", + "InitialPathTooltip": "(Obbligatorio) Percorso iniziale", + "InitialFileChooserTooltip": "Apri esplora file", + "LastBackup": "Ultimo backup", + "SingleBackupButton": "Backup Singolo", + "AutoBackupButtonON": "Backup Automatico (ON)", + "AutoBackupButtonOFF": "Backup Automatico (OFF)", + "DestinationPathTooltip": "(Obbligatorio) Percorso di destinazione", + "MaxBackupsToKeep": "Massimo numero di backup da mantenere", + "SingleBackupTooltip": "Esegui il backup", + "AutoBackupTooltip": "Attiva/Disattiva backup automatico", + "NotesTooltip": "(Opzionale) Descrizione del backup", + "DestinationFileChooserTooltip": "Apri esplora file", + "BackupNameTooltip": "(Obbligatorio) Nome del backup", + "Notes": "Note", + "PageSubtitleCreate": "Crea backup", + "PageSubtitleEdit": "Modifica backup", + "PageSubtitleInfo": "Informazioni backup", + "PageSubtitleSettings": "Impostazioni backup", + "Paths": "Percorsi", + "BackupNamePlaceholder": "Nome backup (univoco)", + "InitialPathPlaceholder": "Percorso di origine es. C:\\Users\\Admin\\Documents", + "DestinationPathPlaceholder": "Cartella di destinazione es. D:\\Backup" + }, + "HistoryLogs": { + "HistoryLogsTitle": "Registro cronologia", + "HistoryLogsDescription": "Qui puoi trovare i log dell'applicazione, utili per la risoluzione dei problemi e per comprendere il comportamento dell'applicazione nel tempo." + }, + "About": { + "AboutSystemInformation": "Informazioni di sistema", + "AboutMessageBody": "Backup Manager è un'applicazione semplice e potente progettata per automatizzare il backup di cartelle e sottocartelle.

Gli utenti possono pianificare backup automatici oppure eseguire backup manuali in qualsiasi momento.

La cronologia dei backup viene salvata in modo sicuro, consentendo il pieno controllo sui dati archiviati.

Visita il sito del progetto per maggiori informazioni.

" + }, + "Dashboard": { + "DashboardTitle": "Dashboard analisi backup", + "DashboardCardTotalConfigurations": "Configurazioni backup totali", + "DashboardCardTotalExecutions": "Esecuzioni backup totali", + "DashboardCardSuccessRate": "Tasso di successo", + "DashboardCardAvgDuration": "Durata media backup", + "DashboardCardCompressionRate": "Tasso di compressione", + "DashboardChartExecutions": "Esecuzioni backup", + "DashboardChartAvgDuration": "Durata media backup (min)" + }, + "Settings": { + "SettingsLayoutTab": "Layout", + "SettingsStyleTab": "Stile", + "SettingsWindowsLayout": "Layout finestra", + "SettingsWindowsRight": "Da destra a sinistra", + "SettingsWindowsFull": "Contenuto finestra completo", + "SettingsDrawerLayout": "Layout pannello", + "SettingsDrawerLeft": "Sinistra", + "SettingsDrawerLeading": "Inizio", + "SettingsDrawerTrailing": "Fine", + "SettingsDrawerRight": "Destra", + "SettingsDrawerTop": "Alto", + "SettingsDrawerBottom": "Basso", + "SettingsModalOption": "Opzione modale predefinita", + "SettingsModalAnimation": "Abilita animazione", + "SettingsModalClose": "Chiudi premendo ESC", + "SettingsLanguagesLayout": "Lingua", + "SettingsAccentLayout": "Colore accento", + "SettingsColorPickerLayout": "Selettore colore", + "SettingsDrawerLineLayout": "Stile linea pannello", + "SettingsDrawerLineCurved": "Linea curva", + "SettingsDrawerDotLine": "Linea tratteggiata", + "SettingsLineStyleLayout": "Opzione stile linea", + "SettingsLineStyleRettangle": "Rettangolo", + "SettingsLineStyleEllipse": "Ellisse", + "SettingsLineStyleLine": "Linea", + "SettingsLineStyleCurved": "Curva", + "SettingsColorOptionLayout": "Opzione colore", + "SettingsColorOptionPainted": "Colora la linea selezionata", + "SettingsThemes": "Temi", + "SettingsThemeAll": "Tutti", + "SettingsThemeLight": "Chiaro", + "SettingsThemeDark": "Scuro" + }, + "SearchBar": { + "SearcTitle": "Cerca...", + "SearchNoRecent": "Nessuna ricerca recente", + "SearchNoResult": "Nessun risultato per", + "SearchFavorite": "Preferiti", + "SearchRecent": "Recenti" + }, + "Toast": { + "BackupEditedOk": "Backup aggiornato con successo", + "BackupCreatedOk": "Backup creato con successo", + "BackupDeletedOk": "Backup eliminato con successo", + "BackupReplacedOk": "Backup esistente sostituito con successo", + "BackupDeletedError": "Impossibile eliminare il backup", + "BackupReplacedError": "Impossibile sostituire il backup", + "InvalidTime": "Intervallo di tempo non valido", + "SubscriptionExpiring": "Il tuo abbonamento Backup Manager scadrà presto. Contattaci per rinnovarlo", + "SubscriptionExpired": "Il tuo abbonamento Backup Manager è scaduto. Contattaci per rinnovarlo", + "LanguageChange": "Alcune modifiche avranno effetto dopo il riavvio dell'applicazione", + "MissingDataLoginError": "Compila tutti i campi obbligatori", + "WrongEmailLoginError": "L'indirizzo email fornito non è valido. Inseriscine uno corretto", + "LoginOk": "Benvenuto! Accesso effettuato con successo", + "ErrorTextClipboard": "Il testo di errore è stato copiato negli appunti", + "CsvExport": "Backup esportati in CSV con successo!", + "CsvExportError": "Errore durante l'esportazione dei backup in CSV", + "CsvExportInvalidFilename": "Nome file non valido. Usa solo caratteri alfanumerici, trattini e underscore", + "NotSupportedEmail": "Il tuo sistema non supporta l'invio diretto di email da questa applicazione", + "UnableToSendEmail": "Impossibile inviare l'email. Riprova più tardi", + "NotSupportedEmailGeneric": "Il tuo sistema non supporta l'invio di email", + "OpeningWebsiteError": "Impossibile aprire la pagina web. Riprova", + "FolderNotExisting": "La cartella non esiste o non è valida", + "BackupNameAlreadyUsed": "Nome backup già utilizzato!", + "BackupAlreadyInProgress": "È già in corso un backup. Non è possibile eseguire backup in parallelo", + "BackupPathsEmptyError": "Il percorso iniziale e il percorso di destinazione non possono essere uguali. Si prega di scegliere percorsi diversi!" + } } diff --git a/src/main/resources/themes/FlatDarkLaf.properties b/src/main/resources/themes/FlatDarkLaf.properties new file mode 100644 index 00000000..8814abfe --- /dev/null +++ b/src/main/resources/themes/FlatDarkLaf.properties @@ -0,0 +1 @@ +Menu.background=tint($Panel.background,3%) \ No newline at end of file diff --git a/src/main/resources/themes/FlatLaf.properties b/src/main/resources/themes/FlatLaf.properties new file mode 100644 index 00000000..53680d46 --- /dev/null +++ b/src/main/resources/themes/FlatLaf.properties @@ -0,0 +1,48 @@ +PasswordField.revealIcon=backupmanager.icons.PasswordRevealIcon + +[style]ToggleButton.revealButton=\ + border:5,0,5,5;\ + toolbar.selectedBackground:null;\ + toolbar.hoverBackground:null;\ + toolbar.pressedBackground:null; + +[style]Label.redBadge=\ + arc:8;\ + border:2,5,2,5;\ + foreground:#f43f5e;\ + background:fade(#f43f5e,10%); + +[style]Label.greenBadge=\ + arc:8;\ + border:2,5,2,5;\ + foreground:#1f9669;\ + background:fade(#1f9669,10%); + +[style]Panel.dashboardBackground=\ + [dark]background:tint($Panel.background,3%);\ + [light]background:tint($Panel.background,25%);\ + border:3,3,3,3,$Component.borderColor,,15; + +# Pagination + +[style]Button.pageItem=\ + margin:5,5,5,5; + +[style]Button.pageItemSelected=\ + margin:5,5,5,5;\ + foreground:$Button.default.foreground;\ + background:$Button.default.background; + +[style]Button.pagePrevious=$[style]Button.pageItem + +[style]Button.pageNext=$[style]Button.pagePrevious + +# Accent colors from flatlaf app + +App.accent.default = #2675BF +App.accent.blue = #007AFF +App.accent.purple = #BF5AF2 +App.accent.red = #FF3B30 +App.accent.orange = #FF9500 +App.accent.yellow = #FFCC00 +App.accent.green = #28CD41 diff --git a/src/main/resources/themes/FlatLightLaf.properties b/src/main/resources/themes/FlatLightLaf.properties new file mode 100644 index 00000000..3a6ca443 --- /dev/null +++ b/src/main/resources/themes/FlatLightLaf.properties @@ -0,0 +1 @@ +Menu.background=tint($Panel.background,20%) \ No newline at end of file diff --git a/src/main/resources/themes/themes.json b/src/main/resources/themes/themes.json new file mode 100644 index 00000000..80ec54e2 --- /dev/null +++ b/src/main/resources/themes/themes.json @@ -0,0 +1,358 @@ +{ + "arc-theme.theme.json": { + "name": "Arc", + "license": "MIT", + "licenseFile": "arc-themes.LICENSE.txt", + "sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea", + "sourceCodePath": "blob/master/arc-theme-idea-light/resources/arc-theme.theme.json" + }, + "arc-theme-orange.theme.json": { + "name": "Arc - Orange", + "license": "MIT", + "licenseFile": "arc-themes.LICENSE.txt", + "sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea", + "sourceCodePath": "blob/master/arc-theme-idea-light/resources/arc-theme-orange.theme.json" + }, + "arc_theme_dark.theme.json": { + "name": "Arc Dark", + "dark": true, + "license": "MIT", + "licenseFile": "arc-themes.LICENSE.txt", + "sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea", + "sourceCodePath": "blob/master/arc-theme-idea-dark/resources/arc_theme_dark.theme.json" + }, + "arc_theme_dark_orange.theme.json": { + "name": "Arc Dark - Orange", + "dark": true, + "license": "MIT", + "licenseFile": "arc-themes.LICENSE.txt", + "sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea", + "sourceCodePath": "blob/master/arc-theme-idea-dark/resources/arc_theme_dark_orange.theme.json" + }, + "Carbon.theme.json": { + "name": "Carbon", + "dark": true, + "license": "Apache License 2.0", + "licenseFile": "arc-themes.LICENSE.txt", + "sourceCodeUrl": "https://github.com/luisfer0793/theme-carbon", + "sourceCodePath": "blob/master/resources/matte_carbon_basics.theme.json" + }, + "Cobalt_2.theme.json": { + "name": "Cobalt 2", + "dark": true, + "license": "MIT", + "licenseFile": "Cobalt_2.LICENSE.txt", + "sourceCodeUrl": "https://github.com/ngehlert/cobalt2", + "sourceCodePath": "blob/master/Cobalt2-UI-Theme/resources/Cobalt_2.theme.json" + }, + "Cyan.theme.json": { + "name": "Cyan light", + "license": "MIT", + "licenseFile": "Cyan.LICENSE.txt", + "sourceCodeUrl": "https://github.com/OlyaB/CyanTheme", + "sourceCodePath": "blob/master/src/Cyan.theme.json" + }, + "DarkFlatTheme.theme.json": { + "name": "Dark Flat", + "dark": true, + "license": "MIT", + "licenseFile": "DarkFlatTheme.LICENSE.txt", + "sourceCodeUrl": "https://github.com/nerzhulart/DarkFlatTheme", + "sourceCodePath": "blob/master/src/DarkFlatTheme.theme.json" + }, + "DarkPurple.theme.json": { + "name": "Dark purple", + "dark": true, + "license": "MIT", + "licenseFile": "DarkPurple.LICENSE.txt", + "sourceCodeUrl": "https://github.com/OlyaB/DarkPurpleTheme", + "sourceCodePath": "blob/master/src/DarkPurple.theme.json" + }, + "Dracula.theme.json": { + "name": "Dracula", + "dark": true, + "license": "MIT", + "licenseFile": "Dracula.LICENSE.txt", + "sourceCodeUrl": "https://github.com/dracula/jetbrains", + "sourceCodePath": "blob/master/src/main/resources/themes/Dracula.theme.json" + }, + "Gradianto_dark_fuchsia.theme.json": { + "name": "Gradianto Dark Fuchsia", + "dark": true, + "license": "MIT", + "licenseFile": "Gradianto.LICENSE.txt", + "sourceCodeUrl": "https://github.com/thvardhan/Gradianto", + "sourceCodePath": "blob/master/src/main/resources/Gradianto_dark_fuchsia.theme.json" + }, + "Gradianto_deep_ocean.theme.json": { + "name": "Gradianto Deep Ocean", + "dark": true, + "license": "MIT", + "licenseFile": "Gradianto.LICENSE.txt", + "sourceCodeUrl": "https://github.com/thvardhan/Gradianto", + "sourceCodePath": "blob/master/src/main/resources/Gradianto_deep_ocean.theme.json" + }, + "Gradianto_midnight_blue.theme.json": { + "name": "Gradianto Midnight Blue", + "dark": true, + "license": "MIT", + "licenseFile": "Gradianto.LICENSE.txt", + "sourceCodeUrl": "https://github.com/thvardhan/Gradianto", + "sourceCodePath": "blob/master/src/main/resources/Gradianto_midnight_blue.theme.json" + }, + "Gradianto_Nature_Green.theme.json": { + "name": "Gradianto Nature Green", + "dark": true, + "license": "MIT", + "licenseFile": "Gradianto.LICENSE.txt", + "sourceCodeUrl": "https://github.com/thvardhan/Gradianto", + "sourceCodePath": "blob/master/src/main/resources/Gradianto_Nature_Green.theme.json" + }, + "Gray.theme.json": { + "name": "Gray", + "license": "MIT", + "licenseFile": "Gray.LICENSE.txt", + "sourceCodeUrl": "https://github.com/OlyaB/GreyTheme", + "sourceCodePath": "blob/master/src/Gray.theme.json" + }, + "gruvbox_dark_hard.theme.json": { + "name": "Gruvbox Dark Hard", + "dark": true, + "license": "MIT", + "licenseFile": "gruvbox_theme.LICENSE.txt", + "sourceCodeUrl": "https://github.com/Vincent-P/gruvbox-intellij-theme", + "sourceCodePath": "blob/master/src/main/resources/gruvbox_dark_hard.theme.json" + }, + "HiberbeeDark.theme.json": { + "name": "Hiberbee Dark", + "dark": true, + "license": "MIT", + "licenseFile": "Hiberbee.LICENSE.txt", + "sourceCodeUrl": "https://github.com/Hiberbee/themes", + "sourceCodePath": "blob/latest/src/intellij/src/main/resources/themes/HiberbeeDark.theme.json" + }, + "HighContrast.theme.json": { + "name": "High contrast", + "dark": true, + "license": "MIT", + "licenseFile": "HighContrast.LICENSE.txt", + "sourceCodeUrl": "https://github.com/OlyaB/HighContrastTheme", + "sourceCodePath": "blob/master/src/HighContrast.theme.json" + }, + "LightFlatTheme.theme.json": { + "name": "Light Flat", + "license": "MIT", + "licenseFile": "LightFlatTheme.LICENSE.txt", + "sourceCodeUrl": "https://github.com/nerzhulart/LightFlatTheme", + "sourceCodePath": "blob/master/src/LightFlatTheme.theme.json" + }, + "MaterialTheme.theme.json": { + "name": "Material Design Dark", + "dark": true, + "license": "MIT", + "licenseFile": "MaterialTheme.LICENSE.txt", + "sourceCodeUrl": "https://github.com/xinkunZ/NotReallyMDTheme", + "sourceCodePath": "blob/master/src/main/resources/MaterialTheme.theme.json" + }, + "Monocai.theme.json": { + "name": "Monocai", + "dark": true, + "license": "MIT", + "licenseFile": "Monocai.LICENSE.txt", + "sourceCodeUrl": "https://github.com/bmikaili/intellij-monocai-theme", + "sourceCodePath": "blob/master/resources/Monocai.theme.json" + }, + "Monokai_Pro.default.theme.json": { + "name": "Monokai Pro", + "dark": true, + "license": "MIT", + "licenseFile": "Monokai_Pro.LICENSE.txt", + "sourceCodeUrl": "https://github.com/subtheme-dev/monokai-pro" + }, + "nord.theme.json": { + "name": "Nord", + "dark": true, + "license": "MIT", + "licenseFile": "nord.LICENSE.txt", + "sourceCodeUrl": "https://github.com/arcticicestudio/nord-jetbrains", + "sourceCodePath": "blob/main/src/nord.theme.json" + }, + "one_dark.theme.json": { + "name": "One Dark", + "dark": true, + "license": "MIT", + "licenseFile": "one_dark.LICENSE.txt", + "sourceCodeUrl": "https://github.com/one-dark/jetbrains-one-dark-theme", + "sourceCodePath": "blob/master/buildSrc/templates/oneDark.template.theme.json" + }, + "SolarizedDark.theme.json": { + "name": "Solarized Dark", + "dark": true, + "license": "The Unlicense", + "licenseFile": "Solarized.LICENSE.txt", + "sourceCodeUrl": "https://github.com/4lex4/intellij-platform-solarized", + "sourceCodePath": "blob/master/resources/SolarizedDark.theme.json" + }, + "SolarizedLight.theme.json": { + "name": "Solarized Light", + "license": "The Unlicense", + "licenseFile": "Solarized.LICENSE.txt", + "sourceCodeUrl": "https://github.com/4lex4/intellij-platform-solarized", + "sourceCodePath": "blob/master/resources/SolarizedLight.theme.json" + }, + "Spacegray.theme.json": { + "name": "Spacegray", + "dark": true, + "license": "MIT", + "licenseFile": "Spacegray.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mturlo/intellij-spacegray", + "sourceCodePath": "blob/master/src/Spacegray.theme.json" + }, + "vuesion_theme.theme.json": { + "name": "Vuesion", + "dark": true, + "license": "MIT", + "licenseFile": "vuesion_theme.LICENSE.txt", + "sourceCodeUrl": "https://github.com/vuesion/intellij-theme", + "sourceCodePath": "blob/master/resources/META-INF/vuesion_theme.theme.json" + }, + "Xcode-Dark.theme.json": { + "name": "Xcode-Dark", + "dark": true, + "license": "MIT", + "licenseFile": "Xcode-Dark.LICENSE.txt", + "sourceCodeUrl": "https://github.com/antelle/intellij-xcode-dark-theme", + "sourceCodePath": "blob/master/resources/Xcode-Dark.theme.json" + }, + "material-theme-ui-lite/Arc Dark.theme.json": { + "name": "Material Theme UI Lite / Arc Dark", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Arc Dark.theme.json" + }, + "material-theme-ui-lite/Atom One Dark.theme.json": { + "name": "Material Theme UI Lite / Atom One Dark", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Atom One Dark.theme.json" + }, + "material-theme-ui-lite/Atom One Light.theme.json": { + "name": "Material Theme UI Lite / Atom One Light", + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Atom One Light.theme.json" + }, + "material-theme-ui-lite/Dracula.theme.json": { + "name": "Material Theme UI Lite / Dracula", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Dracula.theme.json" + }, + "material-theme-ui-lite/GitHub.theme.json": { + "name": "Material Theme UI Lite / GitHub", + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/GitHub.theme.json" + }, + "material-theme-ui-lite/GitHub Dark.theme.json": { + "name": "Material Theme UI Lite / GitHub Dark", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/GitHub Dark.theme.json" + }, + "material-theme-ui-lite/Light Owl.theme.json": { + "name": "Material Theme UI Lite / Light Owl", + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Light Owl.theme.json" + }, + "material-theme-ui-lite/Material Darker.theme.json": { + "name": "Material Theme UI Lite / Material Darker", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Darker.theme.json" + }, + "material-theme-ui-lite/Material Deep Ocean.theme.json": { + "name": "Material Theme UI Lite / Material Deep Ocean", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Deep Ocean.theme.json" + }, + "material-theme-ui-lite/Material Lighter.theme.json": { + "name": "Material Theme UI Lite / Material Lighter", + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Lighter.theme.json" + }, + "material-theme-ui-lite/Material Oceanic.theme.json": { + "name": "Material Theme UI Lite / Material Oceanic", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Oceanic.theme.json" + }, + "material-theme-ui-lite/Material Palenight.theme.json": { + "name": "Material Theme UI Lite / Material Palenight", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Palenight.theme.json" + }, + "material-theme-ui-lite/Monokai Pro.theme.json": { + "name": "Material Theme UI Lite / Monokai Pro", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Monokai Pro.theme.json" + }, + "material-theme-ui-lite/Moonlight.theme.json": { + "name": "Material Theme UI Lite / Moonlight", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Moonlight.theme.json" + }, + "material-theme-ui-lite/Night Owl.theme.json": { + "name": "Material Theme UI Lite / Night Owl", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Night Owl.theme.json" + }, + "material-theme-ui-lite/Solarized Dark.theme.json": { + "name": "Material Theme UI Lite / Solarized Dark", + "dark": true, + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Solarized Dark.theme.json" + }, + "material-theme-ui-lite/Solarized Light.theme.json": { + "name": "Material Theme UI Lite / Solarized Light", + "license": "MIT", + "licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt", + "sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite", + "sourceCodePath": "blob/master/src/main/resources/themes/regular/Solarized Light.theme.json" + } +} \ No newline at end of file diff --git a/src/test/java/test/BackupManagerTest.java b/src/test/java/test/BackupManagerTest.java deleted file mode 100644 index 46748894..00000000 --- a/src/test/java/test/BackupManagerTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package test; - -public class BackupManagerTest { - -} diff --git a/src/test/java/test/BackupTest.java b/src/test/java/test/BackupTest.java deleted file mode 100644 index 62dcb5a9..00000000 --- a/src/test/java/test/BackupTest.java +++ /dev/null @@ -1,3 +0,0 @@ -package test; - -public class BackupTest {} diff --git a/src/test/java/test/LoginServiceTest.java b/src/test/java/test/LoginServiceTest.java new file mode 100644 index 00000000..60cf9c54 --- /dev/null +++ b/src/test/java/test/LoginServiceTest.java @@ -0,0 +1,47 @@ +package test; + +import java.io.IOException; + +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import backupmanager.Entities.User; +import backupmanager.Managers.LanguageManager; +import backupmanager.Services.LoginService; +import backupmanager.Utils.AppPreferences; +import backupmanager.database.Database; +import backupmanager.database.DatabasePaths; +import backupmanager.database.TestDatabaseInitializer; + +public class LoginServiceTest { + @BeforeEach + protected void setup() throws Exception { + Database.init(DatabasePaths.getTestDatabasePath()); + TestDatabaseInitializer.init(); + + AppPreferences.init(); + LanguageManager.loadPreferredLanguage(); + } + + @AfterEach + protected void clean() throws IOException { + TestDatabaseInitializer.deleteDatabase(); + } + + @Test + protected void isFirstAccess_shouldBeTrue_whenNoUserExists() { + LoginService loginService = new LoginService(); + boolean isFirstAccess = loginService.isFirstAccess(); + assertTrue(isFirstAccess); + } + + @Test + protected void createNewUser_shouldBeAbleToCreateUser() { + LoginService loginService = new LoginService(); + loginService.createNewUser(new User("TestName", "TestSurname", "test@gmail.com")); + boolean isFirstAccess = loginService.isFirstAccess(); + assertTrue(!isFirstAccess); + } +} diff --git a/src/test/java/test/SqlHelperTest.java b/src/test/java/test/SqlHelperTest.java index 48681d0b..92e415d5 100644 --- a/src/test/java/test/SqlHelperTest.java +++ b/src/test/java/test/SqlHelperTest.java @@ -25,7 +25,8 @@ void toMilliseconds_shouldBeEquals_forValidLocalDate() { @Test void toMilliseconds_shouldReturnTrue_forNullLocalDate() { - long mills = SqlHelper.toMilliseconds(null); + LocalDateTime date = null; + long mills = SqlHelper.toMilliseconds(date); assertTrue(mills == 0); } diff --git a/src/test/java/test/SubscriptionHelperTest.java b/src/test/java/test/SubscriptionHelperTest.java new file mode 100644 index 00000000..c89dd1c8 --- /dev/null +++ b/src/test/java/test/SubscriptionHelperTest.java @@ -0,0 +1,98 @@ +package test; + +import java.io.IOException; +import java.sql.SQLException; +import java.time.LocalDate; +import java.time.LocalDateTime; + +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import backupmanager.Entities.Configurations; +import backupmanager.Entities.Subscription; +import backupmanager.Enums.ConfigKey; +import backupmanager.Enums.SubscriptionCreationType; +import backupmanager.Enums.SubscriptionStatus; +import backupmanager.Helpers.SubscriptionHelper; +import backupmanager.database.Database; +import backupmanager.database.DatabasePaths; +import backupmanager.database.Repositories.SubscriptionRepository; +import backupmanager.database.TestDatabaseInitializer; + +class SubscriptionHelperTest { + + private static final String CONFIG = "src/main/resources/res/config/config.json"; + + @BeforeEach + protected void setup() throws Exception { + Database.init(DatabasePaths.getTestDatabasePath()); + TestDatabaseInitializer.init(); + ConfigKey.loadFromJson(CONFIG); + } + + @AfterEach + protected void clean() throws IOException { + TestDatabaseInitializer.deleteDatabase(); + } + + @Test + protected void getSubscriptionStatus_shouldBeEquals_forExpiredSubscripion() throws SQLException { + createSubscriptionByStatus(SubscriptionStatus.EXPIRED); + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + assertEquals(SubscriptionStatus.EXPIRED, status); + } + + @Test + protected void getSubscriptionStatus_shouldBeEquals_forActiveSubscripion() throws SQLException { + createSubscriptionByStatus(SubscriptionStatus.ACTIVE); + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + assertEquals(SubscriptionStatus.ACTIVE, status); + } + + @Test + protected void getSubscriptionStatus_shouldBeEquals_forExpiringSubscripion() throws SQLException { + createSubscriptionByStatus(SubscriptionStatus.EXPIRATION); + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + assertEquals(SubscriptionStatus.EXPIRATION, status); + } + + @Test + protected void getSubscriptionStatus_shouldBeEquals_forNoneSubscripion() throws SQLException { + createSubscriptionByStatus(SubscriptionStatus.NONE); + SubscriptionStatus status = SubscriptionHelper.getSubscriptionStatus(); + assertEquals(SubscriptionStatus.NONE, status); + } + + @Test + protected void getSubscriptionStatusTranslated_shouldBeTrue_forSubscriptionValidStatus() { + String translationActive = SubscriptionHelper.getSubscriptionStatusTranslated(SubscriptionStatus.ACTIVE); + String translationExpiration= SubscriptionHelper.getSubscriptionStatusTranslated(SubscriptionStatus.EXPIRATION); + String translationExpired = SubscriptionHelper.getSubscriptionStatusTranslated(SubscriptionStatus.EXPIRED); + assertTrue(!translationActive.isBlank() && !translationExpiration.isEmpty() && !translationExpired.isEmpty()); + } + + @Test + protected void getSubscriptionStatusTranslated_shouldBeTrue_forNoNeddedSubscriptionStatus() { + String translation = SubscriptionHelper.getSubscriptionStatusTranslated(SubscriptionStatus.NONE); + assertTrue(translation.isEmpty()); + } + + private void createSubscriptionByStatus(SubscriptionStatus status) throws SQLException { + LocalDate start = LocalDate.now().plusDays(-1); + LocalDate end = LocalDate.now(); + Configurations.setSubscriptionNedded(true); + switch (status) { + case ACTIVE -> end = end.plusDays(30); + case EXPIRATION -> end = end.plusDays(5); + case EXPIRED -> end = end.plusDays(-1); + case NONE -> Configurations.setSubscriptionNedded(false); + } + + Subscription sub = new Subscription(0, LocalDateTime.now(), start, end, SubscriptionCreationType.MANUAL); + SubscriptionRepository.deleteSubscriptions(); + SubscriptionRepository.insertSubscription(sub); + } +} diff --git a/src/test/java/test/repositories/BackupConfigurationRepositoryTest.java b/src/test/java/test/repositories/BackupConfigurationRepositoryTest.java index fe5766d5..e658ff93 100644 --- a/src/test/java/test/repositories/BackupConfigurationRepositoryTest.java +++ b/src/test/java/test/repositories/BackupConfigurationRepositoryTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test; import backupmanager.Entities.ConfigurationBackup; +import backupmanager.Exceptions.BackupDeletionException; import backupmanager.database.Database; import backupmanager.database.DatabasePaths; import backupmanager.database.Repositories.BackupConfigurationRepository; @@ -41,7 +42,7 @@ protected void insertBackup_shuldBeEquals_fetchFromBackupName() { } @Test - protected void deleteBackup_shuldBeTrue_afterDelete() { + protected void deleteBackup_shuldBeTrue_afterDelete() throws BackupDeletionException { ConfigurationBackup backup = BackupConfigurationRepository.getBackupByName(backups.get(1).getName()); BackupConfigurationRepository.deleteBackup(backup.getId()); ConfigurationBackup backupDeleted = BackupConfigurationRepository.getBackupById(backup.getId()); diff --git a/src/test/java/test/repositories/ConfigurationsRepositoryTest.java b/src/test/java/test/repositories/ConfigurationsRepositoryTest.java deleted file mode 100644 index e5db56b4..00000000 --- a/src/test/java/test/repositories/ConfigurationsRepositoryTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package test.repositories; - -import java.io.IOException; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import backupmanager.Entities.Confingurations; -import backupmanager.Enums.LanguagesEnum; -import backupmanager.Enums.ThemesEnum; -import backupmanager.database.Database; -import backupmanager.database.DatabasePaths; -import backupmanager.database.TestDatabaseInitializer; - -public class ConfigurationsRepositoryTest { - @BeforeEach - protected void setup() throws Exception { - Database.init(DatabasePaths.getTestDatabasePath()); - TestDatabaseInitializer.init(); - - Confingurations.loadAllConfigurations(); - - buildAndReloadConfigurations(); - } - - @AfterEach - protected void clean() throws IOException { - TestDatabaseInitializer.deleteDatabase(); - } - - @Test - protected void equals_shouldReturnTrue_forSameLanguage() throws IOException { - assertEquals(LanguagesEnum.DEU, Confingurations.getLanguage()); - } - - @Test - protected void equals_shouldReturnTrue_forSameTheme() throws IOException { - assertEquals(ThemesEnum.CARBON, Confingurations.getTheme()); - } - - private void buildAndReloadConfigurations() throws IOException { - buildValidConfigurationsObject(); - realodConfigurations(); - } - - private void buildValidConfigurationsObject() { - Confingurations.setLanguage(LanguagesEnum.DEU); - Confingurations.setTheme(ThemesEnum.CARBON.getThemeName()); - } - - private void realodConfigurations() { - Confingurations.updateAllConfigurations(); - Confingurations.loadAllConfigurations(); - } -}