/*********************************************************************************** * * Copyright (c) 2013-2015 ControlsFX, Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the BSD 3-Clause License which * accompany this distribution. * * The BSD 3-Clause License is available at * http://opensource.org/licenses/BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of ControlsFX, any associated website, nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Contributors: * * ControlsFX - initial implementation * Kamil Baczkowicz - minor changes to work with Java dialogs, derivative work created from ControlsFX (http://fxexperience.com/controlsfx/) * */ package pl.baczkowicz.spy.ui.controls; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.concurrent.Worker.State; import javafx.geometry.Insets; import javafx.scene.control.Alert; import javafx.scene.control.ProgressBar; import javafx.scene.layout.Region; public class WorkerProgressPane extends Region { private Worker<?> worker; private boolean dialogVisible = false; private boolean cancelDialogShow = false; private ChangeListener<Worker.State> stateListener = new ChangeListener<Worker.State>() { @Override public void changed(ObservableValue<? extends State> observable, State old, State value) { switch (value) { case CANCELLED: case FAILED: case SUCCEEDED: if (!dialogVisible) { cancelDialogShow = true; } else if (old == State.SCHEDULED || old == State.RUNNING) { end(); } break; case SCHEDULED: begin(); break; default: // no-op } } }; public final void setWorker(final Worker<?> newWorker) { if (newWorker != worker) { if (worker != null) { worker.stateProperty().removeListener(stateListener); end(); } worker = newWorker; if (newWorker != null) { newWorker.stateProperty().addListener(stateListener); if (newWorker.getState() == Worker.State.RUNNING || newWorker.getState() == Worker.State.SCHEDULED) { // It is already running begin(); } } } } // If the progress indicator changes, then we need to re-initialize // If the worker changes, we need to re-initialize private final Alert dialog; private final ProgressBar progressBar; public WorkerProgressPane(Alert dialog) { this.dialog = dialog; this.progressBar = new ProgressBar(); progressBar.setMaxWidth(Double.MAX_VALUE); getChildren().add(progressBar); if (worker != null) { progressBar.progressProperty().bind(worker.progressProperty()); } } private void begin() { // Platform.runLater needs to be used to show the dialog because // the call begin() is going to be occurring when the worker is // notifying state listeners about changes. If Platform.runLater // is not used, the call to show() will cause the worker to get // blocked during notification and it will prevent the worker // from performing any additional notification for state changes. // // Sine the dialog is hidden as a result of a change in worker // state, calling show() without wrapping it in Platform.runLater // will cause the progress dialog to run forever when the dialog // is attached to workers that start out with a state of READY. // // This also creates a case where the worker's state can change // to finished before the dialog is shown, resulting in an // an attempt to hide the dialog before it is shown. It's // necessary to track whether or not this occurs, so flags are // set to indicate if the dialog is visible and if if the call // to show should still be allowed. cancelDialogShow = false; Platform.runLater(new Runnable() { @Override public void run() { if (!cancelDialogShow) { progressBar.progressProperty().bind( worker.progressProperty()); dialogVisible = true; dialog.show(); } } }); } private void end() { progressBar.progressProperty().unbind(); dialogVisible = false; dialog.hide(); dialog.close(); } @Override protected void layoutChildren() { if (progressBar != null) { Insets insets = getInsets(); double w = getWidth() - insets.getLeft() - insets.getRight(); double h = getHeight() - insets.getTop() - insets.getBottom(); double prefH = progressBar.prefHeight(-1); double x = insets.getLeft() + (w - w) / 2.0; double y = insets.getTop() + (h - prefH) / 2.0; progressBar.resizeRelocate(x, y, w, prefH); } } }