diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java index 518cc10..176cfef 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java @@ -477,13 +477,19 @@ public final class GuiConfigurationEditorWorkspace { private final GuiPromptEditorTab promptEditorTab; /** - * Hint banner shown at the top of the configuration tab while a processing run is - * active. Visible + managed state are flipped from the batch run tab's listener when - * the running flag toggles. + * Hint banner shown at the top of the configuration tab while a processing run or + * the automatic scheduler is active. Visible + managed state are controlled by + * {@link #applyConfigTabLockState()}. */ final Label configurationLockBanner = new Label( "Konfiguration während eines laufenden Verarbeitungslaufs nicht editierbar"); + /** + * {@code true} while the automatic scheduler is in any non-{@code STOPPED} state. + * Updated by {@link #updateLockState(SchedulerStatus)} from the 1 Hz refresh timeline. + */ + private boolean schedulerLockActive = false; + /** * Reference to the configuration tab so the running-state listener can disable its * content while a batch run is active. @@ -695,23 +701,41 @@ public final class GuiConfigurationEditorWorkspace { } /** - * Applies the "batch run active" UI lock state to the configuration tab and the - * action bar. + * Applies the "batch run active" UI lock state to the configuration tab. *
- * While a run is active the configuration editor is made non-interactive, the lock - * banner is shown at the top of Tab 1, and the main action buttons (Neu, Öffnen, - * Speichern, Speichern unter) are disabled. When the run ends, the locks are - * released and the editor returns to its normal state. + * Delegates to {@link #applyConfigTabLockState()} so that both the batch-run lock + * and the scheduler lock are evaluated together. Called whenever the batch-run + * running state changes. */ void applyBatchRunLockState() { - boolean running = batchRunTab != null && batchRunTab.isRunning(); - configurationLockBanner.setVisible(running); - configurationLockBanner.setManaged(running); - sectionsBox.setDisable(running); - newButton.setDisable(running); - openButton.setDisable(running); - saveButton.setDisable(running); - saveAsButton.setDisable(running); + applyConfigTabLockState(); + } + + /** + * Evaluates the combined lock state (batch run active or scheduler active) + * and applies it to the configuration tab. + *
+ * When either source is locked the banner is shown, all input sections are disabled + * and the action buttons (Neu, Öffnen, Speichern, Speichern unter) are disabled. + * The banner text describes the dominant lock source. + */ + private void applyConfigTabLockState() { + boolean batchRunning = batchRunTab != null && batchRunTab.isRunning(); + boolean locked = batchRunning || schedulerLockActive; + + String bannerText = schedulerLockActive + ? "⚠ Konfiguration gesperrt – Scheduler läuft (oder Lauf aktiv)." + + " Scheduler beenden bzw. Lauf abwarten um Änderungen vorzunehmen." + : "Konfiguration während eines laufenden Verarbeitungslaufs nicht editierbar"; + + configurationLockBanner.setText(bannerText); + configurationLockBanner.setVisible(locked); + configurationLockBanner.setManaged(locked); + sectionsBox.setDisable(locked); + newButton.setDisable(locked); + openButton.setDisable(locked); + saveButton.setDisable(locked); + saveAsButton.setDisable(locked); } /** @@ -1116,17 +1140,17 @@ public final class GuiConfigurationEditorWorkspace { /** * Aktualisiert den Sperr-Zustand des Konfig-Tabs anhand des aktuellen Scheduler-Status. *
- * Muss auf dem JavaFX Application Thread aufgerufen werden. Wird von - * {@link #onSchedulerStatusRefresh} und der Status-Refresh-Timeline (1 Hz) indirekt - * aufgerufen. + * Setzt {@link #schedulerLockActive} und ruft {@link #applyConfigTabLockState()} auf, + * sodass Banner, Eingabefelder und Aktionsbuttons des Konfig-Tabs sofort in den + * korrekten Zustand versetzt werden. *
- * Die konkrete Reaktion (z. B. Banner-Anzeige und Speichern-Button-Sperre während ein - * Scheduler-Lauf aktiv ist) wird in einem späteren Implementierungsschritt ergänzt. + * Muss auf dem JavaFX Application Thread aufgerufen werden. * * @param status aktueller Scheduler-Status; darf nicht {@code null} sein */ public void updateLockState(SchedulerStatus status) { - // Stub – Implementierung folgt in späterem Schritt + schedulerLockActive = status.state().isActive(); + applyConfigTabLockState(); } /** diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java index 6037816..df11ba0 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java @@ -8,6 +8,7 @@ import javafx.application.Application; import javafx.application.Platform; import javafx.event.EventHandler; import javafx.scene.Scene; +import javafx.scene.control.Alert; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; @@ -110,6 +111,9 @@ public class PdfUmbenennerGuiApplication extends Application { installTrayCloseHandler(primaryStage, workspace); } + // Scheduler-Close-Guard als äußerste Schicht: verhindert Beenden während Scheduler aktiv + installSchedulerCloseGuard(primaryStage); + primaryStage.setMaximized(true); primaryStage.show(); @@ -180,6 +184,39 @@ public class PdfUmbenennerGuiApplication extends Application { return new MenuBar(databaseMenu); } + /** + * Legt den Scheduler-Close-Guard als äußerste Schicht des Close-Request-Handlers an. + *
+ * Ist kein {@link de.gecheckt.pdf.umbenenner.application.port.in.SchedulerControlUseCase}
+ * vorhanden, bleibt der bestehende Handler unverändert. Ist der Scheduler aktiv
+ * (Zustand != {@code STOPPED}), wird das Schließen verhindert und ein
+ * Informationsdialog angezeigt. Ist der Scheduler gestoppt, wird der bisherige
+ * Handler (SystemTray + Workspace-Dirty-Guard) aufgerufen.
+ *
+ * @param stage das primäre Fenster; darf nicht {@code null} sein
+ */
+ private void installSchedulerCloseGuard(Stage stage) {
+ guiStartupContext.schedulerControlUseCase().ifPresent(uc -> {
+ EventHandler
- * Muss auf dem JavaFX Application Thread aufgerufen werden. Wird von der
- * zentralen Status-Refresh-Timeline (1 Hz) aufgerufen.
+ * Deaktiviert den Starten-Button und setzt einen erklärenden Tooltip, solange
+ * der Scheduler aktiv ist. Wenn der Scheduler gestoppt ist, wird der normale
+ * Button-Zustand wiederhergestellt (Starten erlaubt sofern kein Lauf läuft).
*
- * Die konkrete Reaktion (z. B. Deaktivierung des Starten-Buttons während ein
- * Scheduler-Lauf aktiv ist) wird in einem späteren Implementierungsschritt ergänzt.
+ * Muss auf dem JavaFX Application Thread aufgerufen werden.
*
* @param status aktueller Scheduler-Status; darf nicht {@code null} sein
*/
public void updateSchedulerState(SchedulerStatus status) {
- // Stub – Implementierung folgt in späterem Schritt
+ schedulerActive = status.state().isActive();
+ startButton.setDisable(runningProperty.get() || schedulerActive);
+ startButton.setTooltip(new Tooltip(schedulerActive
+ ? "Manuelle Läufe sind während aktivem Scheduler nicht möglich."
+ : GuiTooltipTexts.BATCHRUN_STARTEN));
}
// -------------------------------------------------------------------------
@@ -1485,7 +1492,7 @@ public final class GuiBatchRunTab {
private void updateButtonStates() {
boolean running = runningProperty.get();
- startButton.setDisable(running);
+ startButton.setDisable(running || schedulerActive);
if (!running) {
cancelButton.setDisable(true);
} else {