boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sun, 11 Oct 2015 07:24:32 +0200
changeset 1008 c535c36881af
parent 1006 a0f79e32d526
permissions -rw-r--r--
#255831: BrowserBuilder.showAndWait can be used multiple times with JavaFX presenter. Demonstrated by running the boot-fx tests in 'fork once' mode.
jaroslav@288
     1
/**
jaroslav@358
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@288
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jaroslav@288
     5
 *
jaroslav@358
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jaroslav@358
     7
 * Other names may be trademarks of their respective owners.
jaroslav@288
     8
 *
jaroslav@358
     9
 * The contents of this file are subject to the terms of either the GNU
jaroslav@358
    10
 * General Public License Version 2 only ("GPL") or the Common
jaroslav@358
    11
 * Development and Distribution License("CDDL") (collectively, the
jaroslav@358
    12
 * "License"). You may not use this file except in compliance with the
jaroslav@358
    13
 * License. You can obtain a copy of the License at
jaroslav@358
    14
 * http://www.netbeans.org/cddl-gplv2.html
jaroslav@358
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jaroslav@358
    16
 * specific language governing permissions and limitations under the
jaroslav@358
    17
 * License.  When distributing the software, include this License Header
jaroslav@358
    18
 * Notice in each file and include the License file at
jaroslav@358
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jaroslav@358
    20
 * particular file as subject to the "Classpath" exception as provided
jaroslav@358
    21
 * by Oracle in the GPL Version 2 section of the License file that
jaroslav@358
    22
 * accompanied this code. If applicable, add the following below the
jaroslav@358
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jaroslav@358
    24
 * your own identifying information:
jaroslav@358
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jaroslav@358
    26
 *
jaroslav@358
    27
 * Contributor(s):
jaroslav@358
    28
 *
jaroslav@358
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jaroslav@551
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jaroslav@358
    31
 *
jaroslav@358
    32
 * If you wish your version of this file to be governed by only the CDDL
jaroslav@358
    33
 * or only the GPL Version 2, indicate your decision by adding
jaroslav@358
    34
 * "[Contributor] elects to include this software in this distribution
jaroslav@358
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jaroslav@358
    36
 * single choice of license, a recipient has the option to distribute
jaroslav@358
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jaroslav@358
    38
 * to extend the choice of license to its licensees as provided above.
jaroslav@358
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jaroslav@358
    40
 * Version 2 license, then the option applies only if the new code is
jaroslav@358
    41
 * made subject to such option by the copyright holder.
jaroslav@288
    42
 */
jaroslav@362
    43
package org.netbeans.html.boot.fx;
jaroslav@288
    44
jaroslav@288
    45
import java.net.URL;
jtulach@731
    46
import java.util.ResourceBundle;
jaroslav@288
    47
import java.util.concurrent.CountDownLatch;
jaroslav@288
    48
import java.util.concurrent.Executors;
jaroslav@318
    49
import java.util.logging.Level;
jaroslav@318
    50
import java.util.logging.Logger;
jtulach@953
    51
import java.util.prefs.Preferences;
jaroslav@288
    52
import javafx.application.Application;
jaroslav@288
    53
import javafx.application.Platform;
jaroslav@288
    54
import javafx.beans.value.ChangeListener;
jaroslav@288
    55
import javafx.beans.value.ObservableValue;
jaroslav@288
    56
import javafx.concurrent.Worker;
jaroslav@288
    57
import javafx.event.ActionEvent;
jaroslav@288
    58
import javafx.event.EventHandler;
jaroslav@288
    59
import javafx.geometry.Insets;
jaroslav@288
    60
import javafx.geometry.Pos;
jtulach@941
    61
import javafx.geometry.Rectangle2D;
jaroslav@288
    62
import javafx.scene.Scene;
jaroslav@288
    63
import javafx.scene.control.Button;
jtulach@733
    64
import javafx.scene.control.TextField;
jaroslav@288
    65
import javafx.scene.layout.BorderPane;
jtulach@732
    66
import javafx.scene.layout.HBox;
jaroslav@288
    67
import javafx.scene.layout.VBox;
jaroslav@288
    68
import javafx.scene.text.Text;
jtulach@733
    69
import javafx.scene.web.PromptData;
jaroslav@288
    70
import javafx.scene.web.WebEvent;
jaroslav@288
    71
import javafx.scene.web.WebView;
jaroslav@288
    72
import javafx.stage.Modality;
jtulach@941
    73
import javafx.stage.Screen;
jaroslav@288
    74
import javafx.stage.Stage;
jtulach@953
    75
import javafx.stage.Window;
jtulach@953
    76
import javafx.stage.WindowEvent;
jtulach@732
    77
import javafx.util.Callback;
jaroslav@288
    78
jaroslav@534
    79
/** This is an implementation class, to implement browser builder API. Just
jaroslav@534
    80
 * include this JAR on classpath and the browser builder API will find
jaroslav@288
    81
 * this implementation automatically.
jaroslav@288
    82
 */
jaroslav@288
    83
public class FXBrwsr extends Application {
jaroslav@318
    84
    private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
jaroslav@288
    85
    private static FXBrwsr INSTANCE;
jaroslav@288
    86
    private static final CountDownLatch FINISHED = new CountDownLatch(1);
jaroslav@288
    87
    private BorderPane root;
jaroslav@288
    88
jaroslav@288
    89
    public static synchronized WebView findWebView(final URL url, final FXPresenter onLoad) {
jaroslav@288
    90
        if (INSTANCE == null) {
jtulach@953
    91
            final String callee = findCalleeClassName();
jaroslav@288
    92
            Executors.newFixedThreadPool(1).submit(new Runnable() {
jaroslav@288
    93
                @Override
jaroslav@288
    94
                public void run() {
jtulach@1008
    95
                    if (!Platform.isFxApplicationThread()) {
jtulach@1008
    96
                        try {
jtulach@1008
    97
                            Platform.runLater(this);
jtulach@1008
    98
                        } catch (IllegalStateException ex) {
jtulach@1008
    99
                            try {
jtulach@1008
   100
                                FXBrwsr.launch(FXBrwsr.class, callee);
jtulach@1008
   101
                            } catch (Throwable t) {
jtulach@1008
   102
                                t.printStackTrace();
jtulach@1008
   103
                            } finally {
jtulach@1008
   104
                                FINISHED.countDown();
jtulach@1008
   105
                            }
jtulach@1008
   106
                        }
jtulach@1008
   107
                    } else {
jtulach@1008
   108
                        FXBrwsr brwsr = new FXBrwsr();
jtulach@1008
   109
                        brwsr.start(new Stage(), callee);
jtulach@1008
   110
                        INSTANCE = brwsr;
jaroslav@288
   111
                        FINISHED.countDown();
jaroslav@288
   112
                    }
jaroslav@288
   113
                }
jaroslav@288
   114
            });
jaroslav@288
   115
        }
jaroslav@288
   116
        while (INSTANCE == null) {
jaroslav@288
   117
            try {
jaroslav@288
   118
                FXBrwsr.class.wait();
jaroslav@288
   119
            } catch (InterruptedException ex) {
jaroslav@288
   120
                // wait more
jaroslav@288
   121
            }
jaroslav@288
   122
        }
jaroslav@288
   123
        if (!Platform.isFxApplicationThread()) {
jaroslav@288
   124
            final WebView[] arr = {null};
jaroslav@288
   125
            final CountDownLatch waitForResult = new CountDownLatch(1);
jaroslav@288
   126
            Platform.runLater(new Runnable() {
jaroslav@288
   127
                @Override
jaroslav@288
   128
                public void run() {
jaroslav@288
   129
                    arr[0] = INSTANCE.newView(url, onLoad);
jaroslav@288
   130
                    waitForResult.countDown();
jaroslav@288
   131
                }
jaroslav@288
   132
            });
jaroslav@288
   133
            for (;;) {
jaroslav@288
   134
                try {
jaroslav@288
   135
                    waitForResult.await();
jaroslav@288
   136
                    break;
jaroslav@288
   137
                } catch (InterruptedException ex) {
jaroslav@318
   138
                    LOG.log(Level.INFO, null, ex);
jaroslav@288
   139
                }
jaroslav@288
   140
            }
jaroslav@288
   141
            return arr[0];
jaroslav@288
   142
        } else {
jaroslav@288
   143
            return INSTANCE.newView(url, onLoad);
jaroslav@288
   144
        }
jaroslav@288
   145
    }
jtulach@952
   146
jtulach@863
   147
    static synchronized Stage findStage() throws InterruptedException {
jtulach@863
   148
        while (INSTANCE == null) {
jtulach@863
   149
            FXBrwsr.class.wait();
jtulach@863
   150
        }
jtulach@863
   151
        return INSTANCE.stage;
jtulach@863
   152
    }
jtulach@952
   153
jtulach@730
   154
    private Stage stage;
jaroslav@288
   155
jaroslav@288
   156
    @Override
jaroslav@288
   157
    public void start(Stage primaryStage) throws Exception {
jtulach@1008
   158
        start(primaryStage, this.getParameters().getRaw().get(0));
jtulach@1008
   159
    }
jtulach@1008
   160
jtulach@1008
   161
    final void start(Stage primaryStage, String callee) {
jtulach@863
   162
        BorderPane r = new BorderPane();
jtulach@1008
   163
        Object[] arr = findInitialSize(callee);
jtulach@953
   164
        Scene scene = new Scene(r, (Double)arr[2], (Double)arr[3]);
jtulach@863
   165
        primaryStage.setScene(scene);
jtulach@863
   166
        this.root = r;
jtulach@863
   167
        this.stage = primaryStage;
jaroslav@288
   168
        synchronized (FXBrwsr.class) {
jaroslav@288
   169
            INSTANCE = this;
jaroslav@288
   170
            FXBrwsr.class.notifyAll();
jaroslav@288
   171
        }
jtulach@953
   172
        primaryStage.setX((Double)arr[0]);
jtulach@953
   173
        primaryStage.setY((Double)arr[1]);
jtulach@953
   174
        if (arr[4] != null) {
jtulach@953
   175
            scene.getWindow().setOnCloseRequest((EventHandler<WindowEvent>) arr[4]);
jtulach@953
   176
        }
jtulach@1006
   177
        if (Boolean.getBoolean("fxpresenter.headless")) {
jtulach@1006
   178
            return;
jtulach@1006
   179
        }
jaroslav@288
   180
        primaryStage.show();
jaroslav@288
   181
    }
jaroslav@288
   182
jtulach@953
   183
    static String findCalleeClassName() {
jtulach@953
   184
        StackTraceElement[] frames = new Exception().getStackTrace();
jtulach@953
   185
        for (StackTraceElement e : frames) {
jtulach@953
   186
            String cn = e.getClassName();
jtulach@953
   187
            if (cn.startsWith("org.netbeans.html.")) { // NOI18N
jtulach@953
   188
                continue;
jtulach@953
   189
            }
jtulach@953
   190
            if (cn.startsWith("net.java.html.")) { // NOI18N
jtulach@953
   191
                continue;
jtulach@953
   192
            }
jtulach@953
   193
            if (cn.startsWith("java.")) { // NOI18N
jtulach@953
   194
                continue;
jtulach@953
   195
            }
jtulach@953
   196
            if (cn.startsWith("javafx.")) { // NOI18N
jtulach@953
   197
                continue;
jtulach@953
   198
            }
jtulach@953
   199
            if (cn.startsWith("com.sun.")) { // NOI18N
jtulach@953
   200
                continue;
jtulach@953
   201
            }
jtulach@953
   202
            return cn;
jtulach@953
   203
        }
jtulach@953
   204
        return "org.netbeans.html"; // NOI18N
jtulach@953
   205
    }
jtulach@953
   206
jtulach@953
   207
    private static Object[] findInitialSize(String callee) {
jtulach@953
   208
        final Preferences prefs = Preferences.userRoot().node(callee.replace('.', '/'));
jtulach@952
   209
        Rectangle2D screen = Screen.getPrimary().getBounds();
jtulach@953
   210
        double x = prefs.getDouble("x", screen.getWidth() * 0.05); // NOI18N
jtulach@953
   211
        double y = prefs.getDouble("y", screen.getHeight() * 0.05); // NOI18N
jtulach@953
   212
        double width = prefs.getDouble("width", screen.getWidth() * 0.9); // NOI18N
jtulach@953
   213
        double height = prefs.getDouble("height", screen.getHeight() * 0.9); // NOI18N
jtulach@953
   214
jtulach@953
   215
        Object[] arr = {
jtulach@953
   216
            x, y, width, height, null
jtulach@953
   217
        };
jtulach@953
   218
jtulach@953
   219
        if (!callee.equals("org.netbeans.html")) { // NOI18N
jtulach@953
   220
            arr[4] = new EventHandler<WindowEvent>() {
jtulach@953
   221
                @Override
jtulach@953
   222
                public void handle(WindowEvent event) {
jtulach@953
   223
                    Window window = (Window) event.getSource();
jtulach@953
   224
                    prefs.putDouble("x", window.getX()); // NOI18N
jtulach@953
   225
                    prefs.putDouble("y", window.getY()); // NOI18N
jtulach@953
   226
                    prefs.putDouble("width", window.getWidth()); // NOI18N
jtulach@953
   227
                    prefs.putDouble("height", window.getHeight()); // NOI18N
jtulach@953
   228
                }
jtulach@953
   229
            };
jtulach@953
   230
        }
jtulach@953
   231
jtulach@953
   232
        return arr;
jtulach@952
   233
    }
jtulach@952
   234
jaroslav@288
   235
    private WebView newView(final URL url, final FXPresenter onLoad) {
jaroslav@288
   236
        final WebView view = new WebView();
jaroslav@288
   237
        view.setContextMenuEnabled(false);
jtulach@1008
   238
        Stage newStage;
jtulach@1008
   239
        BorderPane bp;
jtulach@1008
   240
        if (root == null) {
jtulach@1008
   241
            newStage = new Stage();
jtulach@1008
   242
            newStage.initOwner(stage);
jtulach@1008
   243
            bp = new BorderPane();
jtulach@1008
   244
            newStage.setScene(new Scene(bp));
jtulach@1008
   245
            newStage.show();
jtulach@1008
   246
        } else {
jtulach@1008
   247
            bp = root;
jtulach@1008
   248
            newStage = stage;
jtulach@1008
   249
            root = null;
jtulach@1008
   250
        }
jtulach@952
   251
jtulach@1008
   252
        attachHandlers(view, newStage);
jtulach@1008
   253
        bp.setCenter(view);
jaroslav@288
   254
        final Worker<Void> w = view.getEngine().getLoadWorker();
jaroslav@288
   255
        w.stateProperty().addListener(new ChangeListener<Worker.State>() {
jtulach@636
   256
            private String previous;
jtulach@952
   257
jaroslav@288
   258
            @Override
jaroslav@288
   259
            public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
jaroslav@288
   260
                if (newState.equals(Worker.State.SUCCEEDED)) {
jtulach@636
   261
                    if (checkValid()) {
jtulach@636
   262
                        FXConsole.register(view.getEngine());
jtulach@636
   263
                        onLoad.onPageLoad();
jtulach@636
   264
                    }
jaroslav@288
   265
                }
jaroslav@288
   266
                if (newState.equals(Worker.State.FAILED)) {
jaroslav@288
   267
                    throw new IllegalStateException("Failed to load " + url);
jaroslav@288
   268
                }
jaroslav@288
   269
            }
jtulach@636
   270
            private boolean checkValid() {
jtulach@636
   271
                final String crnt = view.getEngine().getLocation();
jtulach@636
   272
                if (previous != null && !previous.equals(crnt)) {
jtulach@636
   273
                    w.stateProperty().removeListener(this);
jtulach@636
   274
                    return false;
jtulach@636
   275
                }
jtulach@636
   276
                previous = crnt;
jtulach@636
   277
                return true;
jtulach@636
   278
            }
jtulach@952
   279
jaroslav@288
   280
        });
jtulach@863
   281
        class Title implements ChangeListener<String> {
jtulach@863
   282
jtulach@863
   283
            private String title;
jtulach@863
   284
jtulach@863
   285
            public Title() {
jtulach@863
   286
                super();
jtulach@863
   287
            }
jtulach@863
   288
jtulach@863
   289
            @Override
jtulach@863
   290
            public void changed(ObservableValue<? extends String> ov, String t, String t1) {
jtulach@863
   291
                title = view.getEngine().getTitle();
jtulach@863
   292
                if (title != null) {
jtulach@863
   293
                    stage.setTitle(title);
jtulach@863
   294
                }
jtulach@863
   295
            }
jtulach@863
   296
        }
jtulach@863
   297
        final Title x = new Title();
jtulach@863
   298
        view.getEngine().titleProperty().addListener(x);
jtulach@863
   299
        x.changed(null, null, null);
jaroslav@288
   300
        return view;
jaroslav@288
   301
    }
jaroslav@288
   302
jtulach@1008
   303
    private static void attachHandlers(final WebView view, final Stage owner) {
jtulach@1008
   304
        view.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() {
jtulach@1008
   305
            @Override
jtulach@1008
   306
            public void handle(WebEvent<String> t) {
jtulach@1008
   307
                final Stage dialogStage = new Stage();
jtulach@1008
   308
                dialogStage.initModality(Modality.WINDOW_MODAL);
jtulach@1008
   309
                dialogStage.initOwner(owner);
jtulach@1008
   310
                ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
jtulach@1008
   311
                dialogStage.setTitle(r.getString("AlertTitle")); // NOI18N
jtulach@1008
   312
                final Button button = new Button(r.getString("AlertCloseButton")); // NOI18N
jtulach@1008
   313
                final Text text = new Text(t.getData());
jtulach@1008
   314
                VBox box = new VBox();
jtulach@1008
   315
                box.setAlignment(Pos.CENTER);
jtulach@1008
   316
                box.setSpacing(10);
jtulach@1008
   317
                box.setPadding(new Insets(10));
jtulach@1008
   318
                box.getChildren().addAll(text, button);
jtulach@1008
   319
                dialogStage.setScene(new Scene(box));
jtulach@1008
   320
                button.setCancelButton(true);
jtulach@1008
   321
                button.setOnAction(new CloseDialogHandler(dialogStage, null));
jtulach@1008
   322
                dialogStage.centerOnScreen();
jtulach@1008
   323
                dialogStage.showAndWait();
jtulach@1008
   324
            }
jtulach@1008
   325
        });
jtulach@1008
   326
        view.getEngine().setConfirmHandler(new Callback<String, Boolean>() {
jtulach@1008
   327
            @Override
jtulach@1008
   328
            public Boolean call(String question) {
jtulach@1008
   329
                final Stage dialogStage = new Stage();
jtulach@1008
   330
                dialogStage.initModality(Modality.WINDOW_MODAL);
jtulach@1008
   331
                dialogStage.initOwner(owner);
jtulach@1008
   332
                ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
jtulach@1008
   333
                dialogStage.setTitle(r.getString("ConfirmTitle")); // NOI18N
jtulach@1008
   334
                final Button ok = new Button(r.getString("ConfirmOKButton")); // NOI18N
jtulach@1008
   335
                final Button cancel = new Button(r.getString("ConfirmCancelButton")); // NOI18N
jtulach@1008
   336
                final Text text = new Text(question);
jtulach@1008
   337
                final Insets ins = new Insets(10);
jtulach@1008
   338
                final VBox box = new VBox();
jtulach@1008
   339
                box.setAlignment(Pos.CENTER);
jtulach@1008
   340
                box.setSpacing(10);
jtulach@1008
   341
                box.setPadding(ins);
jtulach@1008
   342
                final HBox buttons = new HBox(10);
jtulach@1008
   343
                buttons.getChildren().addAll(ok, cancel);
jtulach@1008
   344
                buttons.setAlignment(Pos.CENTER);
jtulach@1008
   345
                buttons.setPadding(ins);
jtulach@1008
   346
                box.getChildren().addAll(text, buttons);
jtulach@1008
   347
                dialogStage.setScene(new Scene(box));
jtulach@1008
   348
                ok.setCancelButton(false);
jtulach@1008
   349
jtulach@1008
   350
                final boolean[] res = new boolean[1];
jtulach@1008
   351
                ok.setOnAction(new CloseDialogHandler(dialogStage, res));
jtulach@1008
   352
                cancel.setCancelButton(true);
jtulach@1008
   353
                cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
jtulach@1008
   354
                dialogStage.centerOnScreen();
jtulach@1008
   355
                dialogStage.showAndWait();
jtulach@1008
   356
                return res[0];
jtulach@1008
   357
            }
jtulach@1008
   358
        });
jtulach@1008
   359
        view.getEngine().setPromptHandler(new Callback<PromptData, String>() {
jtulach@1008
   360
            @Override
jtulach@1008
   361
            public String call(PromptData prompt) {
jtulach@1008
   362
                final Stage dialogStage = new Stage();
jtulach@1008
   363
                dialogStage.initModality(Modality.WINDOW_MODAL);
jtulach@1008
   364
                dialogStage.initOwner(owner);
jtulach@1008
   365
                ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
jtulach@1008
   366
                dialogStage.setTitle(r.getString("PromptTitle")); // NOI18N
jtulach@1008
   367
                final Button ok = new Button(r.getString("PromptOKButton")); // NOI18N
jtulach@1008
   368
                final Button cancel = new Button(r.getString("PromptCancelButton")); // NOI18N
jtulach@1008
   369
                final Text text = new Text(prompt.getMessage());
jtulach@1008
   370
                final TextField line = new TextField();
jtulach@1008
   371
                if (prompt.getDefaultValue() != null) {
jtulach@1008
   372
                    line.setText(prompt.getDefaultValue());
jtulach@1008
   373
                }
jtulach@1008
   374
                final Insets ins = new Insets(10);
jtulach@1008
   375
                final VBox box = new VBox();
jtulach@1008
   376
                box.setAlignment(Pos.CENTER);
jtulach@1008
   377
                box.setSpacing(10);
jtulach@1008
   378
                box.setPadding(ins);
jtulach@1008
   379
                final HBox buttons = new HBox(10);
jtulach@1008
   380
                buttons.getChildren().addAll(ok, cancel);
jtulach@1008
   381
                buttons.setAlignment(Pos.CENTER);
jtulach@1008
   382
                buttons.setPadding(ins);
jtulach@1008
   383
                box.getChildren().addAll(text, line, buttons);
jtulach@1008
   384
                dialogStage.setScene(new Scene(box));
jtulach@1008
   385
                ok.setCancelButton(false);
jtulach@1008
   386
jtulach@1008
   387
                final boolean[] res = new boolean[1];
jtulach@1008
   388
                ok.setOnAction(new CloseDialogHandler(dialogStage, res));
jtulach@1008
   389
                cancel.setCancelButton(true);
jtulach@1008
   390
                cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
jtulach@1008
   391
                dialogStage.centerOnScreen();
jtulach@1008
   392
                dialogStage.showAndWait();
jtulach@1008
   393
                return res[0] ? line.getText() : null;
jtulach@1008
   394
            }
jtulach@1008
   395
        });
jtulach@1008
   396
    }
jtulach@1008
   397
jaroslav@288
   398
    static void waitFinished() {
jaroslav@288
   399
        for (;;) {
jaroslav@288
   400
            try {
jaroslav@288
   401
                FINISHED.await();
jaroslav@288
   402
                break;
jaroslav@288
   403
            } catch (InterruptedException ex) {
jaroslav@318
   404
                LOG.log(Level.INFO, null, ex);
jaroslav@288
   405
            }
jaroslav@288
   406
        }
jaroslav@288
   407
    }
jtulach@952
   408
jtulach@732
   409
    private static final class CloseDialogHandler implements EventHandler<ActionEvent> {
jtulach@732
   410
        private final Stage dialogStage;
jtulach@732
   411
        private final boolean[] res;
jtulach@732
   412
jtulach@732
   413
        public CloseDialogHandler(Stage dialogStage, boolean[] res) {
jtulach@732
   414
            this.dialogStage = dialogStage;
jtulach@732
   415
            this.res = res;
jtulach@732
   416
        }
jtulach@732
   417
jtulach@732
   418
        @Override
jtulach@732
   419
        public void handle(ActionEvent t) {
jtulach@732
   420
            dialogStage.close();
jtulach@732
   421
            if (res != null) {
jtulach@732
   422
                res[0] = true;
jtulach@732
   423
            }
jtulach@732
   424
        }
jtulach@732
   425
    }
jtulach@952
   426
jaroslav@288
   427
}