99import javafx .fxml .FXML ;
1010import javafx .geometry .Insets ;
1111import javafx .scene .Node ;
12+ import javafx .scene .control .Accordion ;
1213import javafx .scene .control .Alert ;
1314import javafx .scene .control .Button ;
1415import javafx .scene .control .ButtonBar ;
1516import javafx .scene .control .ButtonType ;
1617import javafx .scene .control .CheckMenuItem ;
18+ import javafx .scene .control .TitledPane ;
1719import javafx .scene .control .ComboBox ;
1820import javafx .scene .control .Dialog ;
1921import javafx .scene .control .Label ;
2527import javafx .scene .control .ScrollPane ;
2628import javafx .scene .control .SplitPane ;
2729import javafx .scene .control .TextField ;
30+ import javafx .scene .control .ToggleButton ;
2831import javafx .scene .input .DragEvent ;
2932import javafx .scene .input .Dragboard ;
3033import javafx .scene .input .KeyCode ;
@@ -112,11 +115,15 @@ public class MainWindowController {
112115 @ FXML private TextField txtPageNumber ;
113116 @ FXML private Label lblPageCount ;
114117 @ FXML private Button btnNextPage ;
115- @ FXML private Button btnClearVisibleSig ;
118+ @ FXML private ToggleButton btnVisibleSig ;
119+ @ FXML private ToggleButton btnTsa ;
116120 @ FXML private Button btnSign ;
117121
118122 // Content area
119123 @ FXML private SplitPane splitPane ;
124+ @ FXML private Accordion sidePanelAccordion ;
125+ @ FXML private TitledPane tsaAccordionPane ;
126+ @ FXML private TitledPane encryptionAccordionPane ;
120127 @ FXML private ScrollPane scrollPane ;
121128 @ FXML private StackPane pdfArea ;
122129 @ FXML private Label lblDropHint ;
@@ -236,12 +243,22 @@ private void initialize() {
236243 } else {
237244 autoPlaceVisibleSignature ();
238245 }
239- updateVisibleSigIndicators ();
240246 updateSigStateBadge ();
241247 });
242248
243- // Bind visible-signature CheckMenuItem bidirectionally to the ViewModel
249+ // Bind the visible-signature controls (CheckMenuItem + toolbar toggle)
250+ // bidirectionally to the ViewModel so they mirror the side-panel checkbox.
244251 menuVisibleSig .selectedProperty ().bindBidirectional (signingVM .visibleProperty ());
252+ btnVisibleSig .selectedProperty ().bindBidirectional (signingVM .visibleProperty ());
253+ btnTsa .selectedProperty ().bindBidirectional (signingVM .tsaEnabledProperty ());
254+
255+ // When TSA is turned on but no URL is configured yet, jump the side-panel
256+ // accordion to the TSA section so the user can fill the required field.
257+ signingVM .tsaEnabledProperty ().addListener ((obs , was , on ) -> {
258+ if (on && isBlank (signingVM .tsaUrlProperty ().get ())) {
259+ expandTsaPane ();
260+ }
261+ });
245262
246263 // Status-bar badge: visible whenever a document is loaded. Its text and
247264 // colour swap based on whether visible signature is on or off.
@@ -256,7 +273,6 @@ private void initialize() {
256273 // Initial state for the visible-signature controls.
257274 // The badge's initial text and style come from FXML (correct for
258275 // visibleProperty=false on startup); listeners take over on state changes.
259- updateVisibleSigIndicators ();
260276 updateOutputPathLabel ();
261277
262278 // Keep overlay sized to match the pdf page view
@@ -385,6 +401,7 @@ private void setDocumentControlsDisabled(boolean disabled) {
385401 txtPageNumber .setDisable (disabled );
386402 btnNextPage .setDisable (disabled );
387403 btnSign .setDisable (disabled );
404+ btnVisibleSig .setDisable (disabled );
388405 menuSign .setDisable (disabled );
389406 menuClose .setDisable (disabled );
390407 menuSaveAs .setDisable (disabled );
@@ -395,20 +412,6 @@ private void setDocumentControlsDisabled(boolean disabled) {
395412 if (signatureSettingsController != null ) {
396413 signatureSettingsController .setVisibleSigCheckBoxDisabled (disabled );
397414 }
398- // Visible-signature controls track both document state and current toggle
399- updateVisibleSigIndicators ();
400- }
401-
402- /**
403- * Refreshes the enabled state of the "clear visible signature" toolbar button.
404- * It is only meaningful when a document is loaded and the visible signature
405- * is currently enabled.
406- */
407- private void updateVisibleSigIndicators () {
408- boolean canClear = documentVM .isDocumentLoaded () && signingVM .visibleProperty ().get ();
409- if (btnClearVisibleSig != null ) {
410- btnClearVisibleSig .setDisable (!canClear );
411- }
412415 }
413416
414417 /**
@@ -517,6 +520,22 @@ private void updateNavButtonState() {
517520 btnNextPage .setDisable (!documentVM .canGoNext ());
518521 }
519522
523+ private void expandTsaPane () {
524+ if (sidePanelAccordion != null && tsaAccordionPane != null ) {
525+ sidePanelAccordion .setExpandedPane (tsaAccordionPane );
526+ }
527+ }
528+
529+ private void expandEncryptionPane () {
530+ if (sidePanelAccordion != null && encryptionAccordionPane != null ) {
531+ sidePanelAccordion .setExpandedPane (encryptionAccordionPane );
532+ }
533+ }
534+
535+ private static boolean isBlank (String s ) {
536+ return s == null || s .trim ().isEmpty ();
537+ }
538+
520539 private void updateStatus (String message ) {
521540 lblStatus .setText (message );
522541 }
@@ -653,11 +672,6 @@ private void onSaveAs() {
653672 }
654673 }
655674
656- @ FXML
657- private void onClearVisibleSig () {
658- signingVM .visibleProperty ().set (false );
659- }
660-
661675 @ FXML
662676 private void onSign () {
663677 if (options == null || !documentVM .isDocumentLoaded ()) {
@@ -669,13 +683,23 @@ private void onSign() {
669683
670684 // Validate encryption-dependent required fields before signing
671685 if (encryptionSettingsController != null && !encryptionSettingsController .isEncryptionConfigValid ()) {
686+ expandEncryptionPane ();
672687 String prefix = encryptionSettingsController .getValidationErrorKeyPrefix ();
673688 showAlert (Alert .AlertType .WARNING ,
674689 RES .get (prefix + ".title" ),
675690 RES .get (prefix + ".text" ));
676691 return ;
677692 }
678693
694+ // Validate TSA: when enabled, the TSA server URL is mandatory.
695+ if (tsaSettingsController != null && !tsaSettingsController .isTsaConfigValid ()) {
696+ expandTsaPane ();
697+ showAlert (Alert .AlertType .WARNING ,
698+ RES .get ("jfx.gui.dialog.missingTsaUrl.title" ),
699+ RES .get ("jfx.gui.dialog.missingTsaUrl.text" ));
700+ return ;
701+ }
702+
679703 // Sync ViewModel to options
680704 signingVM .syncToOptions (options );
681705
@@ -732,9 +756,11 @@ private void onZoomOut() {
732756 private void onZoomFit () {
733757 if (!documentVM .isDocumentLoaded () || documentVM .getCurrentPageImage () == null ) return ;
734758 double imgWidth = documentVM .getCurrentPageImage ().getWidth ();
759+ double imgHeight = documentVM .getCurrentPageImage ().getHeight ();
735760 double viewWidth = scrollPane .getViewportBounds ().getWidth ();
736- if (imgWidth > 0 && viewWidth > 0 ) {
737- documentVM .setZoomLevel (viewWidth / imgWidth );
761+ double viewHeight = scrollPane .getViewportBounds ().getHeight ();
762+ if (imgWidth > 0 && imgHeight > 0 && viewWidth > 0 && viewHeight > 0 ) {
763+ documentVM .setZoomLevel (Math .min (viewWidth / imgWidth , viewHeight / imgHeight ));
738764 }
739765 }
740766
0 commit comments