All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jhotdraw8.application.action.app.ExitAction Maven / Gradle / Ivy

/*
 * @(#)ExitAction.java
 * Copyright © 2023 The authors and contributors of JHotDraw. MIT License.
 */
package org.jhotdraw8.application.action.app;

import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.input.DataFormat;
import org.jhotdraw8.annotation.NonNull;
import org.jhotdraw8.annotation.Nullable;
import org.jhotdraw8.application.Activity;
import org.jhotdraw8.application.Application;
import org.jhotdraw8.application.ApplicationLabels;
import org.jhotdraw8.application.FileBasedActivity;
import org.jhotdraw8.application.action.AbstractApplicationAction;
import org.jhotdraw8.application.controls.urichooser.FileURIChooser;
import org.jhotdraw8.application.controls.urichooser.URIChooser;
import org.jhotdraw8.application.resources.Resources;
import org.jhotdraw8.base.net.UriUtil;
import org.jhotdraw8.fxbase.concurrent.SimpleWorkState;
import org.jhotdraw8.fxbase.concurrent.WorkState;
import org.jhotdraw8.icollection.ChampMap;

import java.net.URI;
import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.function.Supplier;

import static org.jhotdraw8.application.action.file.AbstractSaveFileAction.SAVE_CHOOSER_FACTORY_KEY;
import static org.jhotdraw8.application.action.file.AbstractSaveFileAction.SAVE_CHOOSER_KEY;

/**
 * Exits the application after letting the user review and possibly save all
 * unsaved views.
 *
 * @author Werner Randelshofer
 */
public class ExitAction extends AbstractApplicationAction {

    public static final String ID = "application.exit";
    private Node oldFocusOwner;
    private @Nullable FileBasedActivity unsavedView;

    /**
     * Creates a new instance.
     *
     * @param app the application
     */
    public ExitAction(Application app) {
        super(app);
        ApplicationLabels.getResources().configureAction(this, ID);
    }

    @Override
    protected void onActionPerformed(@NonNull ActionEvent event, @NonNull Application app) {

        WorkState workState = new SimpleWorkState<>(getLabel());
        app.addDisabler(workState);
        int unsavedViewsCount = 0;
        int disabledViewsCount = 0;
        FileBasedActivity documentToBeReviewed = null;
        URI unsavedURI = null;
        for (Activity pr : app.getActivities()) {
            FileBasedActivity p = (FileBasedActivity) pr;
            if (p.isDisabled()) {
                disabledViewsCount++;
            }
            if (p.isModified()) {
                if (!p.isDisabled()) {
                    documentToBeReviewed = p;
                }
                unsavedURI = p.getURI();
                unsavedViewsCount++;
            }
        }
        if (unsavedViewsCount > 0 && documentToBeReviewed == null) {
            // Silently abort, if no view can be reviewed.
            app.removeDisabler(workState);
            return;
        }

        final Resources labels = ApplicationLabels.getResources();
        switch (unsavedViewsCount) {
        case 0: {
            doExit(workState);
            break;
        }
        case 1: {
            reviewNext(workState);
            break;
        }
        default: {
            ButtonType[] options = { //
                    new ButtonType(labels.getString("application.exit.reviewChangesOption.text"), ButtonBar.ButtonData.YES),//
                    new ButtonType(labels.getString("application.exit.cancelOption.text"), ButtonBar.ButtonData.CANCEL_CLOSE), //
                    new ButtonType(labels.getString("application.exit.discardChangesOption.text"), ButtonBar.ButtonData.NO)//
            };
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION,//
                    labels.getString("application.exit.doYouWantToReview.details"),
                    options);
            alert.setHeaderText(labels.getFormatted("application.exit.doYouWantToReview.message", unsavedViewsCount));
            Optional result = alert.showAndWait();
            if (result.isPresent()) {
                switch (result.get().getButtonData()) {
                default:
                case CANCEL_CLOSE:
                    app.removeDisabler(workState);
                    break;
                case NO:
                    app.exit();
                    break;
                case YES:
                    unsavedView = documentToBeReviewed;
                    reviewChanges(workState);
                    break;
                }
            } else {
                app.removeDisabler(workState);
            }
        }
        }
    }

    protected @Nullable URIChooser getChooser(FileBasedActivity view) {
        URIChooser chooser = app.get(SAVE_CHOOSER_KEY);
        if (chooser == null) {
            Supplier factory = app.get(SAVE_CHOOSER_FACTORY_KEY);
            chooser = factory == null ? new FileURIChooser(FileURIChooser.Mode.SAVE) : factory.get();
            app.set(SAVE_CHOOSER_KEY, chooser);
        }
        return chooser;
    }

    protected void reviewChanges(WorkState workState) {
        FileBasedActivity unsavedViewLocalVariable = this.unsavedView;
        if (unsavedViewLocalVariable != null && !unsavedViewLocalVariable.isDisabled()) {
            final Resources labels = ApplicationLabels.getResources();
            oldFocusOwner = unsavedViewLocalVariable.getNode().getScene().getFocusOwner();
            unsavedViewLocalVariable.removeDisabler(workState);
            URI unsavedURI = unsavedViewLocalVariable.getURI();
            ButtonType[] options = {
                    new ButtonType(labels.getString("application.exit.saveOption.text"), ButtonData.YES),//
                    new ButtonType(labels.getString("application.exit.cancelOption.text"), ButtonData.CANCEL_CLOSE),//
                    new ButtonType(labels.getString("application.exit.dontSaveOption.text"), ButtonData.NO)//
            };
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION,
                    labels.getString("application.exit.doYouWantToSave.details"),
                    options);
            alert.getDialogPane().setMaxWidth(640.0);
            alert.setHeaderText(labels.getFormatted("application.exit.doYouWantToSave.message", //
                    unsavedViewLocalVariable.getTitle(), unsavedViewLocalVariable.getDisambiguation()));
            unsavedViewLocalVariable.getNode().getScene().getWindow().requestFocus();
            alert.initOwner(unsavedViewLocalVariable.getNode().getScene().getWindow());
            Optional result = alert.showAndWait();
            if (result.isPresent()) {
                switch (result.get().getButtonData()) {
                default:
                case CANCEL_CLOSE:
                    unsavedViewLocalVariable.removeDisabler(workState);
                    getApplication().removeDisabler(workState);
                    break;
                case NO:
                    getApplication().getActivities().remove(unsavedViewLocalVariable);
                    unsavedViewLocalVariable.removeDisabler(workState);
                    reviewNext(workState);
                    break;
                case YES:
                    saveChangesAndReviewNext(workState);
                    break;
                }
            } else {
                unsavedViewLocalVariable.removeDisabler(workState);
                getApplication().removeDisabler(workState);
            }
        } else {
            getApplication().removeDisabler(workState);
        }
    }

    protected void saveChangesAndReviewNext(WorkState workState) {
        final FileBasedActivity v = unsavedView;
        if (v.getURI() == null) {
            URIChooser chooser = getChooser(v);
            URI uri = chooser.showDialog(unsavedView.getNode());
            if (uri != null) {
                saveToFileAndReviewNext(uri, chooser.getDataFormat(), workState);

            } else {
                v.removeDisabler(workState);
                if (oldFocusOwner != null) {
                    oldFocusOwner.requestFocus();
                }
                getApplication().removeDisabler(workState);
            }
        } else {
            saveToFileAndReviewNext(v.getURI(), v.getDataFormat(), workState);
        }
    }

    protected void reviewNext(WorkState workState) {
        int unsavedViewsCount = 0;
        FileBasedActivity documentToBeReviewed = null;
        for (Activity pr : getApplication().getActivities()) {
            FileBasedActivity p = (FileBasedActivity) pr;
            if (p.isModified()) {
                if (!p.isDisabled()) {
                    documentToBeReviewed = p;
                }
                unsavedViewsCount++;
            }
        }
        if (unsavedViewsCount == 0) {
            doExit(workState);
        } else if (documentToBeReviewed != null) {
            unsavedView = documentToBeReviewed;
            reviewChanges(workState);
        } else {
            getApplication().removeDisabler(workState);
            //System.out.println("exit silently aborted");
        }
    }

    protected void saveToFile(final @NonNull URI uri, final DataFormat format, WorkState workState) {
        final FileBasedActivity v = unsavedView;
        if (v == null) {
            return;
        }
        v.write(uri, format, ChampMap.of(), workState).handle((result, exception) -> {
            if (exception instanceof CancellationException) {
                v.removeDisabler(this);
                if (oldFocusOwner != null) {
                    oldFocusOwner.requestFocus();
                }
            } else if (exception != null) {
                String message = (exception.getMessage() != null) ? exception.getMessage() : exception.toString();
                Resources labels = ApplicationLabels.getResources();
                Alert alert = new Alert(Alert.AlertType.ERROR,
                        labels.getFormatted("file.save.couldntSave.message", UriUtil.getName(uri)) + "

" + ((message == null) ? "" : message)); alert.getDialogPane().setMaxWidth(640.0); alert.showAndWait(); v.removeDisabler(workState); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } } else { v.setURI(uri); v.clearModified(); app.getRecentUris().put(uri, format); } //noinspection ReturnOfNull return null; }); } protected void saveToFileAndReviewNext(final @NonNull URI uri, final DataFormat format, WorkState workState) { final FileBasedActivity v = unsavedView; if (v == null) { return; } v.write(uri, format, ChampMap.of(), workState).handle((result, exception) -> { if (exception instanceof CancellationException) { v.removeDisabler(workState); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } } else if (exception != null) { Throwable value = exception.getCause(); String message = (value != null && value.getMessage() != null) ? value.getMessage() : value.toString(); Resources labels = ApplicationLabels.getResources(); Alert alert = new Alert(Alert.AlertType.ERROR, labels.getFormatted("file.save.couldntSave.message", UriUtil.getName(uri)) + "

" + ((message == null) ? "" : message)); alert.getDialogPane().setMaxWidth(640.0); alert.showAndWait(); v.removeDisabler(workState); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } } else { v.setURI(uri); v.clearModified(); reviewNext(workState); } //noinspection ReturnOfNull return null; }); } protected void doExit(WorkState workState) { for (Activity pr : new ArrayList<>(app.getActivities())) { FileBasedActivity p = (FileBasedActivity) pr; if (!p.isDisabled() && !p.isModified()) { app.getActivities().remove(p); } } if (app.getActivities().isEmpty()) { app.exit(); } else { app.removeDisabler(workState); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy