How to lock Parent Window in JavaFx when Jasper Report is visible in popup

Viewed 366

For creating Jasper Report in JavaFX 11 I am using dynamic reports. When I click on button Jasper Report open and visible inside a popup. But I can click the button as many number of times as I want and the report will open those number of times. But I want to lock parent window once the jasper report is visible so that the parent window is reachable only after closing the popup. The same thing we can achieve in Parent and Child Window as explained in this question but I want to achieve similar thing with Parent as my stage and child being a Jasper Window. Code for the same is mentioned below:-

JavaFxJasperReportsDemo.java

package demo;

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import net.sf.dynamicreports.jasper.builder.JasperReportBuilder;
import net.sf.dynamicreports.report.builder.DynamicReports;
import net.sf.dynamicreports.report.builder.column.Columns;
import net.sf.dynamicreports.report.builder.component.Components;
import net.sf.dynamicreports.report.builder.datatype.DataTypes;
import net.sf.dynamicreports.report.constant.HorizontalTextAlignment;
import net.sf.dynamicreports.report.exception.DRException;

public class JavaFxJasperReportsDemo extends Application{

    @FXML
    private StackPane stackPane;

    public void start(Stage stage) throws Exception{

        try{
            Parent root = FXMLLoader.load(getClass().getResource("/FXMLJavaFXJasperReportsDemo.fxml"));
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.setTitle("Java FX Demo");
            stage.show();
            stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                public void handle(WindowEvent arg0) {
                    Platform.exit();
                }
            });
        }
        catch (Exception e){
            throw e;
        }
    }

    @FXML
    public void loadReport(ActionEvent event) {
        JasperReportBuilder report = DynamicReports.report();
        List<DemoPOJO> lstDemoPOJOs=new ArrayList<DemoPOJO>();
        DemoPOJO demoPOJO=new DemoPOJO();
        demoPOJO.setName("ABC");
        demoPOJO.setCity("Delhi");
        lstDemoPOJOs.add(demoPOJO);
        demoPOJO = new DemoPOJO();
        demoPOJO.setName("XYZ");
        demoPOJO.setCity("Agra");
        lstDemoPOJOs.add(demoPOJO);
        report
        .columns(
                Columns.columnRowNumberColumn("S No"),
                Columns.column("Name", "name", DataTypes.stringType()),
                Columns.column("Address", "city", DataTypes.stringType())
                ).title(
                        Components.text("Demo Java Fx Jasper Reports").
                        setHorizontalTextAlignment(HorizontalTextAlignment.CENTER))
        .pageFooter(Components.pageXofY())
        .setDataSource(lstDemoPOJOs);
        try {
            report.show(false);
        } catch (DRException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args){
        try{
            launch(args);
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}
2 Answers

As of now I don't have a solution based on "reachability" of parent window. But I can provide you an alternate solution to not allow the user to click multiple times. The idea is to disable the scene's root node as soon as you press the button and enabling it only when the report window is closed. Not sure whether this fits your purpose.

reportButton.setOnAction(e -> {
    // Get root node of the scene
    Node sceneRoot = ((Node)e.getTarget()).getScene().getRoot();

    // First things first, disable it !!
    sceneRoot.setDisable(true);

    // Your actual method for showing report
    loadReport(e);
    
    // Get all current AWT windows.
    java.awt.Window[] windows = java.awt.Window.getWindows();

    // Get the recently added window (your report window) and add a close listener.
    windows[windows.length-1].addWindowListener(new java.awt.event.WindowAdapter() {
        public void windowClosing(java.awt.event.WindowEvent evt) {
            // When the window is closed, enable your root node.
            sceneRoot.setDisable(false);
        }
    });
});

Note: The internal implementation of JasperReportBuilder's JasperViewer is a JFrame.

[UPDATE]

Complete working demo below:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import net.sf.dynamicreports.jasper.builder.JasperReportBuilder;
import net.sf.dynamicreports.report.builder.DynamicReports;
import net.sf.dynamicreports.report.builder.column.Columns;
import net.sf.dynamicreports.report.builder.component.Components;
import net.sf.dynamicreports.report.builder.datatype.DataTypes;
import net.sf.dynamicreports.report.constant.HorizontalAlignment;
import net.sf.dynamicreports.report.exception.DRException;

import java.util.ArrayList;
import java.util.List;

public class JavaFxJasperReportsDemo extends Application {

    @FXML
    private StackPane stackPane;

    public void start(Stage stage) throws Exception {

        try {
            StackPane root = new StackPane();
            ContextMenu cm = new ContextMenu();
            cm.getItems().addAll(new MenuItem("Test1"), new MenuItem("Test2"));
            Button reportButton = new Button("Report");
            reportButton.setContextMenu(cm);
            reportButton.setOnAction(e -> {
                // Get root node of the scene
                Node sceneRoot = ((Node) e.getTarget()).getScene().getRoot();

                // First things first, disable it !!
                sceneRoot.setDisable(true);

                // Your actual method for showing report
                loadReport(e);

                // Get all current AWT windows.
                java.awt.Window[] windows = java.awt.Window.getWindows();

                // Get the recently added window (your report window) and add a listener.
                windows[windows.length - 1].addWindowListener(new java.awt.event.WindowAdapter() {
                    public void windowClosing(java.awt.event.WindowEvent evt) {
                        // When the window is closed, enable your root node.
                        sceneRoot.setDisable(false);
                    }
                });
            });
            root.getChildren().add(reportButton);
            Scene scene = new Scene(root, 200, 200);
            stage.setScene(scene);
            stage.setTitle("Java FX Demo");
            stage.show();
            stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                public void handle(WindowEvent arg0) {
                    Platform.exit();
                }
            });


        } catch (Exception e) {
            throw e;
        }
    }

    @FXML
    public void loadReport(ActionEvent event) {
        JasperReportBuilder report = DynamicReports.report();
        List<DemoPOJO> lstDemoPOJOs = new ArrayList<DemoPOJO>();
        DemoPOJO demoPOJO = new DemoPOJO();
        demoPOJO.setName("ABC");
        demoPOJO.setCity("City 1");
        lstDemoPOJOs.add(demoPOJO);
        demoPOJO = new DemoPOJO();
        demoPOJO.setName("XYZ");
        demoPOJO.setCity("City 2");
        lstDemoPOJOs.add(demoPOJO);
        report
                .columns(
                        Columns.columnRowNumberColumn("S No"),
                        Columns.column("Name", "name", DataTypes.stringType()),
                        Columns.column("Address", "city", DataTypes.stringType())
                ).title(
                Components.text("Demo Java Fx Jasper Reports").
                        setHorizontalAlignment(HorizontalAlignment.CENTER))
                //.pageFooter(Components.pageXofY())
                .setDataSource(lstDemoPOJOs);
        try {
            report.show(false);
        } catch (DRException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            launch(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

This is a modified code of what posted by Sai Dandem. It will disable all the open javafx windows and jasper viewer will always on top. When jasper viewer is closed all the windows will be enabled.

public class MakeJasperViewerAsModalWindow {

    public MakeJasperViewerAsModalWindow(JasperViewer jasperViewer) {

        jasperViewer.setAlwaysOnTop(true);

        final java.awt.Window[] windows = java.awt.Window.getWindows();
        final List<Window> windowsfx = Window.getWindows();
        windowsfx.stream().forEach(o -> {

            o.getScene().getRoot().setDisable(true);
        });

        windows[windows.length - 1].addWindowListener(new java.awt.event.WindowAdapter() {

            @Override
            public void windowClosing(java.awt.event.WindowEvent evt) {

                windowsfx.stream().forEach(o -> {

                    o.getScene().getRoot().setDisable(false);
                });
            }
        });
    }
}

it can be used as new MakeJasperViewerAsModalWindow(jasperViewer);

Related