Moved Network into it's own project
This commit is contained in:
parent
61014779dd
commit
aa72b39593
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.FutureTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
class JavaFxUtil {
|
|
||||||
|
|
||||||
public static final javafx.scene.text.Font DEFAULT_FONT = new javafx.scene.text.Font(13);
|
|
||||||
|
|
||||||
|
|
||||||
public static
|
|
||||||
void showOnSameScreenAsMouseCenter(javafx.stage.Window stage) {
|
|
||||||
Point mouseLocation = MouseInfo.getPointerInfo()
|
|
||||||
.getLocation();
|
|
||||||
|
|
||||||
GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation);
|
|
||||||
Rectangle bounds = deviceAtMouse.getDefaultConfiguration()
|
|
||||||
.getBounds();
|
|
||||||
|
|
||||||
stage.setX(bounds.x + bounds.width / 2 - stage.getWidth() / 2);
|
|
||||||
stage.setY(bounds.y + bounds.height / 2 - stage.getHeight() / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
void showOnSameScreenAsMouse(javafx.stage.Window stage) {
|
|
||||||
Point mouseLocation = MouseInfo.getPointerInfo()
|
|
||||||
.getLocation();
|
|
||||||
|
|
||||||
GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation);
|
|
||||||
|
|
||||||
stage.setX(deviceAtMouse.getDefaultConfiguration()
|
|
||||||
.getBounds().x);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void invokeAndWait(Runnable runnable) {
|
|
||||||
// run synchronously on JavaFX thread
|
|
||||||
if (Platform.isFxApplicationThread()) {
|
|
||||||
runnable.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureTask future = new FutureTask(runnable, null);
|
|
||||||
Platform.runLater(future);
|
|
||||||
try {
|
|
||||||
future.get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T invokeAndWait(Callable callable) {
|
|
||||||
// run synchronously on JavaFX thread
|
|
||||||
if (Platform.isFxApplicationThread()) {
|
|
||||||
try {
|
|
||||||
return (T) callable.call();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureTask future = new FutureTask(callable);
|
|
||||||
Platform.runLater(future);
|
|
||||||
try {
|
|
||||||
return (T) future.get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void invokeLater(Runnable runnable) {
|
|
||||||
if (Platform.isFxApplicationThread()) {
|
|
||||||
runnable.run();
|
|
||||||
} else {
|
|
||||||
Platform.runLater(runnable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,489 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.javafx;
|
|
||||||
|
|
||||||
import com.sun.javafx.application.PlatformImpl;
|
|
||||||
import dorkbox.util.JavaFxUtil;
|
|
||||||
import dorkbox.util.NamedThreadFactory;
|
|
||||||
import dorkbox.util.ScreenUtil;
|
|
||||||
import dorkbox.util.SwingUtil;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.value.WritableValue;
|
|
||||||
import javafx.embed.swing.JFXPanel;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is necessary, because JavaFX stage is crap on linux. This offers sort-of the same functionality, but via swing instead.
|
|
||||||
* Annoying caveat. All swing setters MUST happen on the EDT.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public
|
|
||||||
class StageViaSwing {
|
|
||||||
public static final Executor frameDisposer = Executors.newSingleThreadExecutor(new NamedThreadFactory("Swing Disposer",
|
|
||||||
Thread.MIN_PRIORITY,
|
|
||||||
true));
|
|
||||||
|
|
||||||
|
|
||||||
final JFrame frame;
|
|
||||||
final JFXPanel panel;
|
|
||||||
|
|
||||||
private volatile boolean inNestedEventLoop = false;
|
|
||||||
private final CountDownLatch showLatch = new CountDownLatch(1);
|
|
||||||
private final CountDownLatch showAndWaitLatch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
final WritableValue<Float> opacityProperty;
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
|
|
||||||
static
|
|
||||||
StageViaSwing create() {
|
|
||||||
final StageViaSwing[] returnVal = new StageViaSwing[1];
|
|
||||||
|
|
||||||
// this MUST happen on the EDT!
|
|
||||||
SwingUtil.invokeAndWait(() -> {
|
|
||||||
synchronized (returnVal) {
|
|
||||||
returnVal[0] = new StageViaSwing();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
synchronized (returnVal) {
|
|
||||||
return returnVal[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Necessary for us to be able to size our frame based on it's content
|
|
||||||
*/
|
|
||||||
private static Method method;
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
method = Scene.class.getDeclaredMethod("preferredSize");
|
|
||||||
method.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that javafx application thread is started
|
|
||||||
// Note that calling PlatformImpl.startup more than once is OK
|
|
||||||
PlatformImpl.startup(() -> {
|
|
||||||
// No need to do anything here
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private volatile boolean center = false;
|
|
||||||
private volatile double x;
|
|
||||||
private volatile double y;
|
|
||||||
private volatile double width;
|
|
||||||
private volatile double height;
|
|
||||||
private volatile boolean closing;
|
|
||||||
private volatile boolean resizable;
|
|
||||||
|
|
||||||
public
|
|
||||||
void setAlwaysOnTop(final boolean alwaysOnTop) {
|
|
||||||
frame.setAlwaysOnTop(alwaysOnTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnShowAnimation {
|
|
||||||
void doShow();
|
|
||||||
}
|
|
||||||
|
|
||||||
private OnShowAnimation showAnimation = null;
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
void setShowAnimation(final OnShowAnimation showAnimation) {
|
|
||||||
this.showAnimation = showAnimation;
|
|
||||||
}
|
|
||||||
|
|
||||||
StageViaSwing() {
|
|
||||||
frame = new JFrame();
|
|
||||||
panel = new JFXPanel();
|
|
||||||
|
|
||||||
// frame.setLayout(null);
|
|
||||||
frame.setUndecorated(true);
|
|
||||||
frame.setOpacity(0F);
|
|
||||||
frame.add(panel);
|
|
||||||
|
|
||||||
|
|
||||||
opacityProperty = new WritableValue<Float>() {
|
|
||||||
@Override
|
|
||||||
public Float getValue() {
|
|
||||||
return frame.getOpacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValue(Float value) {
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setOpacity(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
frame.addWindowListener(new WindowAdapter() {
|
|
||||||
public void windowOpened(WindowEvent e) {
|
|
||||||
if (showAnimation != null) {
|
|
||||||
// Thread thread = new Thread(() -> {
|
|
||||||
// try {
|
|
||||||
// If this runs now, it will bug out, and flash on the screen before we want it to.
|
|
||||||
// REALLY dumb, but we have to wait for the system to draw the window and finish BEFORE we move it
|
|
||||||
// otherwise, it'll 'flash' onscreen because it will still be in the middle of it's initial "on-show" animation.
|
|
||||||
// Thread.sleep(5000);
|
|
||||||
|
|
||||||
if (!inNestedEventLoop) {
|
|
||||||
renderContents();
|
|
||||||
} else {
|
|
||||||
// notify we are done showing, to prevent race conditions with the JFX app thread
|
|
||||||
// the show method continues
|
|
||||||
showLatch.countDown();
|
|
||||||
}
|
|
||||||
// } catch(InterruptedException ignored) {
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// thread.setDaemon(true);
|
|
||||||
// thread.setName("Window centering");
|
|
||||||
// thread.start();
|
|
||||||
} else if (!inNestedEventLoop) {
|
|
||||||
renderContents();
|
|
||||||
} else {
|
|
||||||
// notify we are done showing, to prevent race conditions with the JFX app thread
|
|
||||||
// the show method continues
|
|
||||||
showLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void renderContents() {
|
|
||||||
sizeToScene();
|
|
||||||
|
|
||||||
SwingUtil.invokeLater(StageViaSwing.this::recheckSize);
|
|
||||||
|
|
||||||
if (showAnimation == null) {
|
|
||||||
opacityProperty.setValue(1F);
|
|
||||||
completeShowTransition();
|
|
||||||
} else {
|
|
||||||
showAnimation.doShow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// absolutely stupid - swing doesn't want to be forced to a certain size, unless specific incantations are performed. These seem to work
|
|
||||||
void recheckSize() {
|
|
||||||
if (frame.getX() != x || frame.getY() != y || frame.getWidth() != width || frame.getHeight() != height) {
|
|
||||||
// System.err.println("FAILED SIZE CHECK");
|
|
||||||
// System.err.println("SIZE: " + width + " : " + height);
|
|
||||||
// System.err.println("actual: " + frame.getWidth() + " " + frame.getHeight());
|
|
||||||
|
|
||||||
final Dimension size = new Dimension((int) width, (int) height);
|
|
||||||
if (!resizable) {
|
|
||||||
frame.setMinimumSize(size);
|
|
||||||
frame.setMaximumSize(size);
|
|
||||||
panel.setMinimumSize(size);
|
|
||||||
panel.setMaximumSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
panel.setPreferredSize(size);
|
|
||||||
frame.setPreferredSize(size);
|
|
||||||
|
|
||||||
if (center) {
|
|
||||||
// same as in screenUtils, but here we set bound instead of just location
|
|
||||||
final Point mouseLocation = MouseInfo.getPointerInfo()
|
|
||||||
.getLocation();
|
|
||||||
|
|
||||||
final GraphicsDevice deviceAtMouse = ScreenUtil.getGraphicsDeviceAt(mouseLocation);
|
|
||||||
final Rectangle bounds = deviceAtMouse.getDefaultConfiguration()
|
|
||||||
.getBounds();
|
|
||||||
|
|
||||||
|
|
||||||
panel.setBounds(bounds.x + (bounds.width / 2) - (int)width / 2,
|
|
||||||
bounds.y + (bounds.height / 2) - (int)height / 2,
|
|
||||||
(int)width,
|
|
||||||
(int)height);
|
|
||||||
|
|
||||||
frame.setBounds(bounds.x + (bounds.width / 2) - (int)width / 2,
|
|
||||||
bounds.y + (bounds.height / 2) - (int)height / 2,
|
|
||||||
(int)width,
|
|
||||||
(int)height);
|
|
||||||
} else {
|
|
||||||
panel.setBounds((int) x,
|
|
||||||
(int) y,
|
|
||||||
(int) width,
|
|
||||||
(int) height);
|
|
||||||
frame.setBounds((int) x,
|
|
||||||
(int) y,
|
|
||||||
(int) width,
|
|
||||||
(int) height);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.pack();
|
|
||||||
frame.revalidate();
|
|
||||||
frame.repaint();
|
|
||||||
|
|
||||||
// System.err.println("recheck SIZE: " + frame.getWidth() + " " + frame.getHeight());
|
|
||||||
// System.err.println("recheck SIZE: " + panel.getWidth() + " " + frame.getHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final
|
|
||||||
void completeShowTransition() {
|
|
||||||
showLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setTitle(final String title) {
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setTitle(title));
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
String getTitle() {
|
|
||||||
return frame.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
void close() {
|
|
||||||
closing = true;
|
|
||||||
|
|
||||||
// "hide" it until we can properly do so.
|
|
||||||
SwingUtil.invokeAndWait(() -> {
|
|
||||||
frame.setOpacity(0F);
|
|
||||||
frame.setBounds(Short.MIN_VALUE, Short.MIN_VALUE, 0, 0);
|
|
||||||
//noinspection deprecation
|
|
||||||
frame.hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
frameDisposer.execute(() -> {
|
|
||||||
// stupid thing flashes on-screen if we run this right away...
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
SwingUtil.invokeLater(frame::dispose);
|
|
||||||
});
|
|
||||||
|
|
||||||
releaseLatch(showAndWaitLatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void releaseLatch(final CountDownLatch latch) {
|
|
||||||
if (inNestedEventLoop) {
|
|
||||||
inNestedEventLoop = false;
|
|
||||||
|
|
||||||
if (!Platform.isFxApplicationThread()) {
|
|
||||||
JavaFxUtil.invokeAndWait(() -> com.sun.javafx.tk.Toolkit.getToolkit().exitNestedEventLoop(StageViaSwing.this, null));
|
|
||||||
} else {
|
|
||||||
com.sun.javafx.tk.Toolkit.getToolkit().exitNestedEventLoop(StageViaSwing.this, null);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setSize(final double width, final double height) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setSize((int)width, (int)height));
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setResizable(final boolean resizable) {
|
|
||||||
this.resizable = resizable;
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setResizable(resizable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setApplicationIcon(final java.awt.Image icon) {
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setIconImage(icon));
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void show(final double x, final double y) {
|
|
||||||
// we want to make sure we go BACK to this location when we show the JFRAME on screen
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
|
|
||||||
show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void showAndWait(final double x, final double y) {
|
|
||||||
// we want to make sure we go BACK to this location when we show the JFRAME on screen
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
|
|
||||||
showAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void show() {
|
|
||||||
SwingUtil.invokeAndWait(() -> {
|
|
||||||
frame.setSize(0, 0);
|
|
||||||
frame.setVisible(false);
|
|
||||||
frame.setOpacity(0f);
|
|
||||||
frame.setBounds(Short.MIN_VALUE, Short.MIN_VALUE, 0, 0);
|
|
||||||
|
|
||||||
frame.revalidate();
|
|
||||||
frame.repaint();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Figure out the size of everything. Because JFXPanel DOES NOT do this.
|
|
||||||
|
|
||||||
// wait until our show animation is complete. There is a small delay out of necessity
|
|
||||||
// false-positive
|
|
||||||
//noinspection Duplicates
|
|
||||||
if (Platform.isFxApplicationThread()) {
|
|
||||||
inNestedEventLoop = true;
|
|
||||||
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setVisible(true));
|
|
||||||
|
|
||||||
try {
|
|
||||||
showLatch.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
inNestedEventLoop = false;
|
|
||||||
|
|
||||||
sizeToScene();
|
|
||||||
|
|
||||||
SwingUtil.invokeAndWait(() -> {
|
|
||||||
if (showAnimation == null) {
|
|
||||||
opacityProperty.setValue(1F);
|
|
||||||
completeShowTransition();
|
|
||||||
} else {
|
|
||||||
showAnimation.doShow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setVisible(true));
|
|
||||||
|
|
||||||
try {
|
|
||||||
showLatch.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void showAndWait() {
|
|
||||||
show();
|
|
||||||
|
|
||||||
// false-positive
|
|
||||||
//noinspection Duplicates
|
|
||||||
if (Platform.isFxApplicationThread()) {
|
|
||||||
inNestedEventLoop = true;
|
|
||||||
com.sun.javafx.tk.Toolkit.getToolkit().enterNestedEventLoop(this);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
showAndWaitLatch.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void initModality(final Dialog.ModalExclusionType modal) {
|
|
||||||
// we take in the javaFX modality, and pass it on to the correct swing version
|
|
||||||
JavaFxUtil.invokeAndWait(() -> frame.setModalExclusionType(modal));
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void sizeToScene() {
|
|
||||||
SwingUtil.invokeAndWait(() -> {
|
|
||||||
frame.revalidate();
|
|
||||||
frame.repaint();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Figure out the size of everything. Because JFXPanel DOES NOT do this.
|
|
||||||
// must be on the FX app thread
|
|
||||||
JavaFxUtil.invokeAndWait(() -> {
|
|
||||||
final Scene scene = panel.getScene();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// use reflection. This is lame, but necessary. must be on the jfx thread
|
|
||||||
method.invoke(scene);
|
|
||||||
|
|
||||||
width = scene.getWidth();
|
|
||||||
height = scene.getHeight();
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
SwingUtil.invokeAndWait(this::recheckSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setScene(final Scene scene) {
|
|
||||||
// must be on the JFX or EDT threads
|
|
||||||
if (!Platform.isFxApplicationThread() && !EventQueue.isDispatchThread()) {
|
|
||||||
JavaFxUtil.invokeAndWait(() -> panel.setScene(scene));
|
|
||||||
} else {
|
|
||||||
panel.setScene(scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
WritableValue<Float> getOpacityProperty() {
|
|
||||||
return opacityProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setLocation(final double x, final double y) {
|
|
||||||
// we want to make sure we go BACK to this location when we show the JFRAME on screen
|
|
||||||
if (x != this.x || y != this.y) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
|
|
||||||
if (!closing) {
|
|
||||||
SwingUtil.invokeAndWait(() -> frame.setLocation((int)x, (int)y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
double getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
double getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void center() {
|
|
||||||
this.center = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
Dimension getSize() {
|
|
||||||
return frame.getSize();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,999 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2014, 2015 ControlsFX
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* MODIFIED BY DORKBOX
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.javafx;
|
|
||||||
|
|
||||||
import dorkbox.util.JavaFxUtil;
|
|
||||||
import dorkbox.util.SwingUtil;
|
|
||||||
import impl.org.controlsfx.ImplUtils;
|
|
||||||
import javafx.animation.*;
|
|
||||||
import javafx.beans.property.*;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.collections.ObservableMap;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.Event;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Dialog;
|
|
||||||
import javafx.scene.control.ToolBar;
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import javafx.scene.input.KeyEvent;
|
|
||||||
import javafx.scene.layout.*;
|
|
||||||
import javafx.scene.text.Font;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
import org.controlsfx.control.PopOver;
|
|
||||||
import org.controlsfx.tools.ValueExtractor;
|
|
||||||
import org.controlsfx.validation.ValidationSupport;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>The API for creating multi-page Wizards, based on JavaFX {@link Dialog} API.<br/>
|
|
||||||
* Wizard can be setup in following few steps:<p/>
|
|
||||||
* <ul>
|
|
||||||
* <li>Design wizard pages by inheriting them from {@link WizardPage}</li>
|
|
||||||
* <li>Define wizard flow by implementing {@link org.controlsfx.dialog.Wizard.Flow}</li>
|
|
||||||
* <li>Create and instance of the Wizard and assign flow to it</li>
|
|
||||||
* <li>Execute the wizard using showAndWait method</li>
|
|
||||||
* <li>Values can be extracted from settings map by calling getSettings
|
|
||||||
* </ul>
|
|
||||||
* <p>For simple, linear wizards, the {@link LinearFlow} can be used.
|
|
||||||
* It is a flow based on a collection of wizard pages. Here is the example:</p>
|
|
||||||
*
|
|
||||||
* <pre>{@code // Create pages. Here for simplicity we just create and instance of WizardPane.
|
|
||||||
* WizardPane page1 = new WizardPane();
|
|
||||||
* WizardPane page2 = new WizardPane();
|
|
||||||
* WizardPane page2 = new WizardPane();
|
|
||||||
*
|
|
||||||
* // create wizard
|
|
||||||
* Wizard wizard = new Wizard();
|
|
||||||
*
|
|
||||||
* // create and assign the flow
|
|
||||||
* wizard.setFlow(new LinearFlow(page1, page2, page3));
|
|
||||||
*
|
|
||||||
* // show wizard and wait for response
|
|
||||||
* wizard.showAndWait().ifPresent(result -> {
|
|
||||||
* if (result == ButtonType.FINISH) {
|
|
||||||
* System.out.println("Wizard finished, settings: " + wizard.getSettings());
|
|
||||||
* }
|
|
||||||
* });}</pre>
|
|
||||||
*
|
|
||||||
* <p>For more complex wizard flows we suggest to create a custom ones, describing page traversal logic.
|
|
||||||
* Here is a simplified example: </p>
|
|
||||||
*
|
|
||||||
* <pre>{@code Wizard.Flow branchingFlow = new Wizard.Flow() {
|
|
||||||
* public Optional<WizardPane> advance(WizardPane currentPage) {
|
|
||||||
* return Optional.of(getNext(currentPage));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* public boolean canAdvance(WizardPane currentPage) {
|
|
||||||
* return currentPage != page3;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private WizardPane getNext(WizardPane currentPage) {
|
|
||||||
* if ( currentPage == null ) {
|
|
||||||
* return page1;
|
|
||||||
* } else if ( currentPage == page1) {
|
|
||||||
* // skipNextPage() does not exist - this just represents that you
|
|
||||||
* // can add a conditional statement here to change the page.
|
|
||||||
* return page1.skipNextPage()? page3: page2;
|
|
||||||
* } else {
|
|
||||||
* return page3;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* };}</pre>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class Wizard {
|
|
||||||
final StageViaSwing stage = StageViaSwing.create();
|
|
||||||
|
|
||||||
private final Text headerText;
|
|
||||||
private final VBox center;
|
|
||||||
|
|
||||||
private final ObservableMap<String, Object> settings = FXCollections.observableHashMap();
|
|
||||||
|
|
||||||
final Stack<WizardPage> pageHistory = new Stack<>();
|
|
||||||
Optional<WizardPage> currentPage = Optional.empty();
|
|
||||||
|
|
||||||
private final BooleanProperty invalidProperty = new SimpleBooleanProperty(false);
|
|
||||||
private final StringProperty invalidPropertyStrings = new SimpleStringProperty();
|
|
||||||
|
|
||||||
|
|
||||||
// Read settings activated by default for backward compatibility
|
|
||||||
private final BooleanProperty readSettingsProperty = new SimpleBooleanProperty(true);
|
|
||||||
|
|
||||||
volatile boolean success = false;
|
|
||||||
|
|
||||||
private final Button BUTTON_PREVIOUS = new Button("Previous");
|
|
||||||
|
|
||||||
private final Button BUTTON_NEXT = new Button("Next");
|
|
||||||
private final EventHandler<Event> BUTTON_NEXT_EVENT_HANDLER = event -> {
|
|
||||||
if (event instanceof KeyEvent) {
|
|
||||||
final KeyCode code = ((KeyEvent)event).getCode();
|
|
||||||
if (code == KeyCode.ENTER || code == KeyCode.SPACE) {
|
|
||||||
event.consume();
|
|
||||||
goNext();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.consume();
|
|
||||||
goNext();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final EventHandler<Event> BUTTON_FINISH_EVENT_HANDLER = event -> {
|
|
||||||
if (event instanceof KeyEvent) {
|
|
||||||
final KeyCode code = ((KeyEvent)event).getCode();
|
|
||||||
if (code == KeyCode.ENTER || code == KeyCode.SPACE) {
|
|
||||||
event.consume();
|
|
||||||
goFinish();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.consume();
|
|
||||||
goFinish();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private volatile boolean useSpecifiedSize = false;
|
|
||||||
|
|
||||||
private final PopOver popOver;
|
|
||||||
private final Text popOverErrorText;
|
|
||||||
private final Font defaultHeaderFont;
|
|
||||||
private VBox graphicRegion;
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Constructors
|
|
||||||
*
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of the wizard with no title.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
Wizard() {
|
|
||||||
this("");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of the wizard with the given title.
|
|
||||||
*
|
|
||||||
* @param title The wizard title.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
Wizard(String title) {
|
|
||||||
stage.center();
|
|
||||||
|
|
||||||
stage.initModality(java.awt.Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
|
|
||||||
setTitle(title);
|
|
||||||
|
|
||||||
BUTTON_PREVIOUS.setDisable(true);
|
|
||||||
BUTTON_NEXT.setDisable(true);
|
|
||||||
|
|
||||||
BUTTON_PREVIOUS.setId("prev-button");
|
|
||||||
BUTTON_NEXT.setId("next-button");
|
|
||||||
|
|
||||||
BUTTON_PREVIOUS.addEventFilter(ActionEvent.ACTION, event -> {
|
|
||||||
event.consume();
|
|
||||||
goPrev();
|
|
||||||
});
|
|
||||||
|
|
||||||
BUTTON_PREVIOUS.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
|
||||||
final KeyCode code = event.getCode();
|
|
||||||
if (code == KeyCode.ENTER || code == KeyCode.SPACE) {
|
|
||||||
event.consume();
|
|
||||||
goPrev();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
popOver = new PopOver();
|
|
||||||
popOver.setDetachable(false);
|
|
||||||
popOver.setDetached(false);
|
|
||||||
popOver.setAutoHide(false);
|
|
||||||
|
|
||||||
popOver.setArrowSize(12);
|
|
||||||
popOver.setArrowIndent(12);
|
|
||||||
popOver.setArrowLocation(PopOver.ArrowLocation.TOP_CENTER);
|
|
||||||
popOver.setCornerRadius(6);
|
|
||||||
|
|
||||||
VBox content = new VBox();
|
|
||||||
content.setPadding(new Insets(10));
|
|
||||||
|
|
||||||
popOverErrorText = new Text();
|
|
||||||
popOverErrorText.setFont(new Font(12));
|
|
||||||
|
|
||||||
content.setPadding(new Insets(20, 10, 0, 10));
|
|
||||||
content.getChildren()
|
|
||||||
.add(popOverErrorText);
|
|
||||||
|
|
||||||
popOver.setContentNode(content);
|
|
||||||
|
|
||||||
invalidPropertyStrings.addListener((observable, oldValue, newValue) -> {
|
|
||||||
validatePopover(newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
Consumer<WizardPage> consumer = new Consumer<WizardPage>() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void accept(final WizardPage currentPage) {
|
|
||||||
if (currentPage.autoFocusNext) {
|
|
||||||
JavaFxUtil.invokeLater(BUTTON_NEXT::requestFocus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
invalidProperty.addListener((ObservableValue<? extends Boolean> o, Boolean ov, Boolean nv) -> {
|
|
||||||
validateActionState();
|
|
||||||
// the value is "invalid", so we want "!invalid"
|
|
||||||
if (ov && !nv) {
|
|
||||||
currentPage.ifPresent(consumer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
BorderPane borderPane = new BorderPane();
|
|
||||||
|
|
||||||
// Auto-sizing spacer
|
|
||||||
Region spacer = new Region();
|
|
||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
|
||||||
|
|
||||||
|
|
||||||
Button cancel = new Button("Cancel");
|
|
||||||
cancel.addEventFilter(ActionEvent.ACTION, event -> {
|
|
||||||
success = false;
|
|
||||||
close();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
ToolBar toolbar = new ToolBar(cancel, spacer, BUTTON_PREVIOUS, BUTTON_NEXT);
|
|
||||||
toolbar.setPadding(new Insets(8));
|
|
||||||
borderPane.setBottom(toolbar);
|
|
||||||
|
|
||||||
headerText = new Text();
|
|
||||||
defaultHeaderFont = new Font(25);
|
|
||||||
headerText.setFont(defaultHeaderFont);
|
|
||||||
|
|
||||||
graphicRegion = new VBox();
|
|
||||||
|
|
||||||
Region spacer2 = new Region();
|
|
||||||
spacer2.setMinWidth(10);
|
|
||||||
|
|
||||||
ToolBar region = new ToolBar(graphicRegion, spacer2, headerText);
|
|
||||||
region.setPadding(new Insets(15, 12, 15, 12));
|
|
||||||
borderPane.setTop(region);
|
|
||||||
|
|
||||||
center = new VBox();
|
|
||||||
center.setMinSize(10, 10);
|
|
||||||
center.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
|
||||||
// center.setStyle("-fx-background-color: #2046ff;");
|
|
||||||
borderPane.setCenter(center);
|
|
||||||
|
|
||||||
Scene scene = new Scene(borderPane);
|
|
||||||
stage.setSize(300, 140);
|
|
||||||
stage.setScene(scene);
|
|
||||||
stage.setResizable(false); // hide the minimize/maximize decorations
|
|
||||||
|
|
||||||
|
|
||||||
scene.setOnKeyPressed(ke -> {
|
|
||||||
if (ke.getCode() == KeyCode.ESCAPE) {
|
|
||||||
success = false;
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//noinspection Duplicates
|
|
||||||
stage.setShowAnimation(() -> {
|
|
||||||
Timeline timeline = new Timeline();
|
|
||||||
timeline.setCycleCount(1);
|
|
||||||
timeline.getKeyFrames()
|
|
||||||
.addAll(new KeyFrame(Duration.millis(500), new KeyValue(stage.getOpacityProperty(), 1F, Interpolator.EASE_OUT)));
|
|
||||||
// have to trigger that our animation is completed and the show() method may continue
|
|
||||||
timeline.setOnFinished(event -> stage.completeShowTransition());
|
|
||||||
timeline.play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void validatePopover(final String newValue) {
|
|
||||||
if (newValue != null) {
|
|
||||||
currentPage.ifPresent(currentPage -> JavaFxUtil.invokeLater(() -> {
|
|
||||||
final PopOver popOver = this.popOver;
|
|
||||||
|
|
||||||
this.popOverErrorText.setText(newValue);
|
|
||||||
|
|
||||||
if (!popOver.isShowing()) {
|
|
||||||
popOver.setX(0);
|
|
||||||
popOver.setY(0);
|
|
||||||
popOver.show(BUTTON_NEXT, -10);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
popOver.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Public API
|
|
||||||
*
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to customize the width and height of the wizard. Must be set before the flow is set
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
void setSize(int width, int height) {
|
|
||||||
stage.setSize(width, height);
|
|
||||||
useSpecifiedSize = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the wizard but does not wait for a user response (in other words, this brings up a non-blocking dialog).
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
void show() {
|
|
||||||
stage.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the wizard and waits for the user response (in other words, brings up a blocking dialog, with the returned value (true=finished
|
|
||||||
* or false=cancel/close)
|
|
||||||
*
|
|
||||||
* @return An true/false depending on how they closed the wizard.
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
boolean showAndWait() {
|
|
||||||
stage.showAndWait();
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void close() {
|
|
||||||
stage.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings map is the place where all data from pages is kept once the user moves on from the page, assuming there is a {@link
|
|
||||||
* ValueExtractor} that is capable of extracting a value out of the various fields on the page.
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
ObservableMap<String, Object> getSettings() {
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Goes to the next page, or finishes the wizard
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void goNext() {
|
|
||||||
JavaFxUtil.invokeLater(() -> {
|
|
||||||
currentPage.ifPresent(pageHistory::push);
|
|
||||||
currentPage = getFlow().advance(currentPage.orElse(null));
|
|
||||||
updatePage(stage, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void goPrev() {
|
|
||||||
currentPage = Optional.ofNullable(pageHistory.isEmpty() ? null : pageHistory.pop());
|
|
||||||
JavaFxUtil.invokeLater(() -> updatePage(stage, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void goFinish() {
|
|
||||||
success = true;
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Properties
|
|
||||||
*
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
// --- title
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the title of the wizard.
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
String getTitle() {
|
|
||||||
return stage.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the Title of the wizard.
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
void setTitle(String title) {
|
|
||||||
stage.setTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- flow
|
|
||||||
/**
|
|
||||||
* The {@link Flow} property represents the flow of pages in the wizard.
|
|
||||||
*/
|
|
||||||
private final
|
|
||||||
ObjectProperty<Flow> flow = new SimpleObjectProperty<Flow>(new LinearFlow()) {
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void invalidated() {
|
|
||||||
updatePage(stage, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void set(Flow flow) {
|
|
||||||
super.set(flow);
|
|
||||||
pageHistory.clear();
|
|
||||||
|
|
||||||
if (flow != null) {
|
|
||||||
currentPage = flow.advance(currentPage.orElse(null));
|
|
||||||
updatePage(stage, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public final
|
|
||||||
ObjectProperty<Flow> flowProperty() {
|
|
||||||
return flow;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently set {@link Flow}, which represents the flow of pages in the wizard.
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
Flow getFlow() {
|
|
||||||
return flow.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link Flow}, which represents the flow of pages in the wizard.
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
void setFlow(Flow flow) {
|
|
||||||
JavaFxUtil.invokeAndWait(() -> this.flow.set(flow));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --- Properties
|
|
||||||
private static final Object USER_DATA_KEY = new Object();
|
|
||||||
|
|
||||||
// A map containing a set of properties for this Wizard
|
|
||||||
private ObservableMap<Object, Object> properties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an observable map of properties on this Wizard for use primarily by application developers - not to be confused with the
|
|
||||||
* {@link #getSettings()} map that represents the values entered by the user into the wizard.
|
|
||||||
*
|
|
||||||
* @return an observable map of properties on this Wizard for use primarily by application developers
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("FieldRepeatedlyAccessedInMethod")
|
|
||||||
public final
|
|
||||||
ObservableMap<Object, Object> getProperties() {
|
|
||||||
if (properties == null) {
|
|
||||||
properties = FXCollections.observableMap(new HashMap<>());
|
|
||||||
}
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests if this Wizard has properties.
|
|
||||||
*
|
|
||||||
* @return true if this Wizard has properties.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
boolean hasProperties() {
|
|
||||||
return properties != null && !properties.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --- UserData
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a previously set Object property, or null if no such property has been set using the {@link #setUserData(Object)} method.
|
|
||||||
*
|
|
||||||
* @return The Object that was previously set, or null if no property has been set or if null was set.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
Object getUserData() {
|
|
||||||
return getProperties().get(USER_DATA_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method for setting a single Object property that can be retrieved at a later date. This is functionally equivalent to
|
|
||||||
* calling the getProperties().put(Object key, Object value) method. This can later be retrieved by calling {@link #getUserData()}.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* The value to be stored - this can later be retrieved by calling {@link #getUserData()}.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void setUserData(Object value) {
|
|
||||||
getProperties().put(USER_DATA_KEY, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the property {@code invalid}.
|
|
||||||
*
|
|
||||||
* @return The validation state
|
|
||||||
*
|
|
||||||
* @see #invalidProperty()
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
boolean isInvalid() {
|
|
||||||
return invalidProperty.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the property {@code invalid}.
|
|
||||||
*
|
|
||||||
* @param invalid
|
|
||||||
* The new validation state {@link #invalidProperty() }
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
void setInvalid(boolean invalid) {
|
|
||||||
invalidProperty.set(invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property for overriding the individual validation state of this {@link org.controlsfx.dialog.Wizard}. Setting {@code invalid} to true
|
|
||||||
* will disable the next/finish Button and the user will not be able to advance to the next page of the {@link
|
|
||||||
* org.controlsfx.dialog.Wizard}. Setting {@code invalid} to false will enable the next/finish Button. <br> <br> For example you can use
|
|
||||||
* the {@link ValidationSupport#invalidProperty()} of a page and bind it to the {@code invalid} property: <br> {@code
|
|
||||||
* wizard.invalidProperty().bind(page.validationSupport.invalidProperty()); }
|
|
||||||
*
|
|
||||||
* @return The validation state property
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
BooleanProperty invalidProperty() {
|
|
||||||
return invalidProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the property {@code readSettings}.
|
|
||||||
*
|
|
||||||
* @return The read-settings state
|
|
||||||
*
|
|
||||||
* @see #readSettingsProperty()
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
boolean isReadSettings() {
|
|
||||||
return readSettingsProperty.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the property {@code readSettings}.
|
|
||||||
*
|
|
||||||
* @param readSettings
|
|
||||||
* The new read-settings state
|
|
||||||
*
|
|
||||||
* @see #readSettingsProperty()
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
void setReadSettings(boolean readSettings) {
|
|
||||||
readSettingsProperty.set(readSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property for overriding the individual read-settings state of this {@link org.controlsfx.dialog.Wizard}. Setting {@code readSettings}
|
|
||||||
* to true will enable the value extraction for this {@link org.controlsfx.dialog.Wizard}. Setting {@code readSettings} to false will
|
|
||||||
* disable the value extraction for this {@link org.controlsfx.dialog.Wizard}.
|
|
||||||
*
|
|
||||||
* @return The readSettings state property
|
|
||||||
*/
|
|
||||||
public final
|
|
||||||
BooleanProperty readSettingsProperty() {
|
|
||||||
return readSettingsProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
* Private implementation
|
|
||||||
**************************************************************************/
|
|
||||||
boolean BUTTON_PREV_INIT = false;
|
|
||||||
boolean BUTTON_NEXT_INIT = false;
|
|
||||||
|
|
||||||
void updatePage(StageViaSwing stage, boolean advancing) {
|
|
||||||
Flow flow = getFlow();
|
|
||||||
if (flow == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialTransition sequentialTransition = new SequentialTransition();
|
|
||||||
sequentialTransition.setCycleCount(1);
|
|
||||||
|
|
||||||
Optional<WizardPage> prevPage = Optional.ofNullable(pageHistory.isEmpty() ? null : pageHistory.peek());
|
|
||||||
prevPage.ifPresent(page -> {
|
|
||||||
// if we are going forward in the wizard, we read in the settings
|
|
||||||
// from the page and store them in the settings map.
|
|
||||||
// If we are going backwards, we do nothing
|
|
||||||
// This is only performed if readSettings is true.
|
|
||||||
if (advancing && isReadSettings()) {
|
|
||||||
readSettings(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
// give the previous wizard page a chance to update the pages list
|
|
||||||
// based on the settings it has received
|
|
||||||
page.onExitingPage(this);
|
|
||||||
|
|
||||||
invalidProperty.unbind();
|
|
||||||
invalidPropertyStrings.unbind();
|
|
||||||
|
|
||||||
invalidProperty.set(false);
|
|
||||||
popOver.hide();
|
|
||||||
|
|
||||||
Timeline timeline = new Timeline();
|
|
||||||
timeline.setCycleCount(1);
|
|
||||||
timeline.getKeyFrames()
|
|
||||||
.addAll(new KeyFrame(Duration.millis(200),
|
|
||||||
new KeyValue(stage.getOpacityProperty(), 0F, Interpolator.EASE_OUT)));
|
|
||||||
|
|
||||||
timeline.setOnFinished(event -> currentPage.ifPresent(currentPage -> {
|
|
||||||
refreshCurrentPage(stage, currentPage);
|
|
||||||
|
|
||||||
SwingUtil.invokeAndWait(() -> SwingUtil.showOnSameScreenAsMouseCenter(stage.frame));
|
|
||||||
|
|
||||||
Timeline timeline2 = new Timeline();
|
|
||||||
timeline2.setCycleCount(1);
|
|
||||||
timeline2.getKeyFrames()
|
|
||||||
.addAll(new KeyFrame(Duration.millis(500),
|
|
||||||
new KeyValue(stage.getOpacityProperty(), 1F, Interpolator.EASE_OUT)));
|
|
||||||
timeline2.play();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
sequentialTransition.getChildren().add(timeline);
|
|
||||||
});
|
|
||||||
|
|
||||||
// only run this if we don't have a prev page, otherwise, we run this at the end of our animation
|
|
||||||
if (!prevPage.isPresent()) {
|
|
||||||
currentPage.ifPresent(currentPage -> refreshCurrentPage(stage, currentPage));
|
|
||||||
}
|
|
||||||
|
|
||||||
sequentialTransition.play();
|
|
||||||
validateActionState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void refreshCurrentPage(final StageViaSwing stage, final WizardPage currentPage) {
|
|
||||||
// put in default actions
|
|
||||||
if (!BUTTON_PREV_INIT) {
|
|
||||||
BUTTON_PREV_INIT = true;
|
|
||||||
BUTTON_PREVIOUS.setDisable(false);
|
|
||||||
}
|
|
||||||
if (!BUTTON_NEXT_INIT) {
|
|
||||||
BUTTON_NEXT_INIT = true;
|
|
||||||
BUTTON_NEXT.setDisable(false);
|
|
||||||
|
|
||||||
BUTTON_NEXT.addEventFilter(ActionEvent.ACTION, BUTTON_NEXT_EVENT_HANDLER);
|
|
||||||
BUTTON_NEXT.addEventFilter(KeyEvent.KEY_PRESSED, BUTTON_NEXT_EVENT_HANDLER);
|
|
||||||
}
|
|
||||||
|
|
||||||
// then give user a chance to modify the default actions
|
|
||||||
currentPage.onEnteringPage(this);
|
|
||||||
|
|
||||||
invalidProperty.bind(currentPage.invalidProperty);
|
|
||||||
invalidPropertyStrings.bind(currentPage.invalidPropertyStrings);
|
|
||||||
|
|
||||||
final Node firstFocusElement = currentPage.firstFocusElement;
|
|
||||||
if (firstFocusElement != null) {
|
|
||||||
JavaFxUtil.invokeLater(() -> {
|
|
||||||
if (isInvalid()) {
|
|
||||||
firstFocusElement.requestFocus();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JavaFxUtil.invokeLater(BUTTON_NEXT::requestFocus);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (isInvalid()) {
|
|
||||||
JavaFxUtil.invokeLater(BUTTON_PREVIOUS::requestFocus);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JavaFxUtil.invokeLater(BUTTON_NEXT::requestFocus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// and then switch to the new pane
|
|
||||||
if (currentPage.headerFont != null) {
|
|
||||||
headerText.setFont(currentPage.headerFont);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
headerText.setFont(defaultHeaderFont);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPage.headerGraphic != null) {
|
|
||||||
graphicRegion.getChildren().setAll(currentPage.headerGraphic);
|
|
||||||
} else {
|
|
||||||
graphicRegion.getChildren().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
headerText.setText(currentPage.headerText);
|
|
||||||
ObservableList<Node> children = center.getChildren();
|
|
||||||
children.clear();
|
|
||||||
children.add(currentPage.anchorPane);
|
|
||||||
|
|
||||||
if (!useSpecifiedSize) {
|
|
||||||
currentPage.anchorPane.autosize();
|
|
||||||
|
|
||||||
if (stage.frame.isShowing()) {
|
|
||||||
stage.sizeToScene();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JavaFxUtil.invokeAndWait(() -> {
|
|
||||||
if (isInvalid()) {
|
|
||||||
validatePopover(currentPage.invalidPropertyStrings.get());
|
|
||||||
} else {
|
|
||||||
popOver.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
void validateActionState() {
|
|
||||||
// Note that we put the 'next' and 'finish' actions at the beginning of
|
|
||||||
// the actions list, so that it takes precedence as the default button,
|
|
||||||
// over, say, cancel. We will probably want to handle this better in the
|
|
||||||
// future...
|
|
||||||
|
|
||||||
if (!getFlow().canAdvance(currentPage.orElse(null))) {
|
|
||||||
BUTTON_NEXT.setText("Finish");
|
|
||||||
BUTTON_NEXT.removeEventFilter(ActionEvent.ACTION, BUTTON_NEXT_EVENT_HANDLER);
|
|
||||||
BUTTON_NEXT.removeEventFilter(KeyEvent.KEY_PRESSED, BUTTON_NEXT_EVENT_HANDLER);
|
|
||||||
|
|
||||||
BUTTON_NEXT.addEventFilter(ActionEvent.ACTION, BUTTON_FINISH_EVENT_HANDLER);
|
|
||||||
BUTTON_NEXT.addEventFilter(KeyEvent.KEY_PRESSED, BUTTON_FINISH_EVENT_HANDLER);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!BUTTON_NEXT.getText()
|
|
||||||
.equals("Next")) {
|
|
||||||
BUTTON_NEXT.addEventFilter(ActionEvent.ACTION, BUTTON_NEXT_EVENT_HANDLER);
|
|
||||||
BUTTON_NEXT.addEventFilter(KeyEvent.KEY_PRESSED, BUTTON_NEXT_EVENT_HANDLER);
|
|
||||||
|
|
||||||
BUTTON_NEXT.removeEventFilter(ActionEvent.ACTION, BUTTON_FINISH_EVENT_HANDLER);
|
|
||||||
BUTTON_NEXT.removeEventFilter(KeyEvent.KEY_PRESSED, BUTTON_FINISH_EVENT_HANDLER);
|
|
||||||
}
|
|
||||||
BUTTON_NEXT.setText("Next");
|
|
||||||
}
|
|
||||||
|
|
||||||
validateButton(BUTTON_PREVIOUS, pageHistory::isEmpty);
|
|
||||||
validateButton(BUTTON_NEXT, invalidProperty::get);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functional design allows to delay condition evaluation until it is actually needed
|
|
||||||
private static
|
|
||||||
void validateButton(Button button, BooleanSupplier condition) {
|
|
||||||
if ( button != null ) {
|
|
||||||
boolean asBoolean = condition.getAsBoolean();
|
|
||||||
button.setDisable(asBoolean);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int settingCounter;
|
|
||||||
|
|
||||||
private
|
|
||||||
void readSettings(WizardPage page) {
|
|
||||||
// for now we cannot know the structure of the page, so we just drill down
|
|
||||||
// through the entire scenegraph (from page.anchorPane down) until we get
|
|
||||||
// to the leaf nodes. We stop only if we find a node that is a
|
|
||||||
// ValueContainer (either by implementing the interface), or being
|
|
||||||
// listed in the internal valueContainers map.
|
|
||||||
|
|
||||||
settingCounter = 0;
|
|
||||||
checkNode(page.anchorPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
boolean checkNode(Node n) {
|
|
||||||
boolean success = readSetting(n);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
// we've added the setting to the settings map and we should stop drilling deeper
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/**
|
|
||||||
* go into children of this node (if possible) and see if we can get
|
|
||||||
* a value from them (recursively) We use reflection to fix
|
|
||||||
* https://bitbucket.org/controlsfx/controlsfx/issue/412 .
|
|
||||||
*/
|
|
||||||
List<Node> children = ImplUtils.getChildren(n, true);
|
|
||||||
|
|
||||||
// we're doing a depth-first search, where we stop drilling down
|
|
||||||
// once we hit a successful read
|
|
||||||
boolean childSuccess = false;
|
|
||||||
for (Node child : children) {
|
|
||||||
childSuccess |= checkNode(child);
|
|
||||||
}
|
|
||||||
return childSuccess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
boolean readSetting(Node n) {
|
|
||||||
if (n == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object setting = ValueExtractor.getValue(n);
|
|
||||||
|
|
||||||
if (setting != null) {
|
|
||||||
// save it into the settings map.
|
|
||||||
// if the node has an id set, we will use that as the setting name
|
|
||||||
String settingName = n.getId();
|
|
||||||
|
|
||||||
// but if the id is not set, we will use a generic naming scheme
|
|
||||||
if (settingName == null || settingName.isEmpty()) {
|
|
||||||
settingName = "page_" /*+ previousPageIndex*/ + ".setting_" + settingCounter; //$NON-NLS-1$ //$NON-NLS-2$
|
|
||||||
}
|
|
||||||
|
|
||||||
getSettings().put(settingName, setting);
|
|
||||||
|
|
||||||
settingCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return setting != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void requestNextFocus() {
|
|
||||||
BUTTON_NEXT.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void requestPrevFocus() {
|
|
||||||
BUTTON_PREVIOUS.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* You can add multiple images of different sizes and JavaFX will pick the one that fits best. Because you have different sizes
|
|
||||||
* in task bar and different in title bar.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void setApplicationIcon(final java.awt.Image applicationIcon) {
|
|
||||||
stage.setApplicationIcon(applicationIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Support classes
|
|
||||||
*
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the page flow of the wizard. It defines only methods required to move forward in the wizard logic, as backward movement is
|
|
||||||
* automatically handled by wizard itself, using internal page history.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
interface Flow {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Advances the wizard to the next page if possible.
|
|
||||||
*
|
|
||||||
* @param currentPage
|
|
||||||
* The current wizard page
|
|
||||||
*
|
|
||||||
* @return {@link Optional} value containing the next wizard page.
|
|
||||||
*/
|
|
||||||
Optional<WizardPage> advance(WizardPage currentPage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if advancing to the next page is possible
|
|
||||||
*
|
|
||||||
* @param currentPage
|
|
||||||
* The current wizard page
|
|
||||||
*
|
|
||||||
* @return true if it is possible to advance to the next page, false otherwise.
|
|
||||||
*/
|
|
||||||
boolean canAdvance(WizardPage currentPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LinearFlow is an implementation of the {@link org.controlsfx.dialog.Wizard.Flow} interface, designed to support the most common type
|
|
||||||
* of wizard flow - namely, a linear wizard page flow (i.e. through all pages in the order that they are specified). Therefore, this
|
|
||||||
* {@link Flow} implementation simply traverses a collections of {@link WizardPage WizardPanes}.
|
|
||||||
* <p>
|
|
||||||
* <p>For example of how to use this API, please refer to the {@link org.controlsfx.dialog.Wizard} documentation</p>
|
|
||||||
*
|
|
||||||
* @see org.controlsfx.dialog.Wizard
|
|
||||||
* @see WizardPage
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
class LinearFlow implements Wizard.Flow {
|
|
||||||
|
|
||||||
private final List<WizardPage> pages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new LinearFlow instance that will allow for stepping through the given collection of {@link WizardPage} instances.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
LinearFlow(Collection<WizardPage> pages) {
|
|
||||||
this.pages = new ArrayList<>(pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new LinearFlow instance that will allow for stepping through the given varargs array of {@link WizardPage} instances.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
LinearFlow(WizardPage... pages) {
|
|
||||||
this(Arrays.asList(pages));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Optional<WizardPage> advance(WizardPage currentPage) {
|
|
||||||
int pageIndex = pages.indexOf(currentPage);
|
|
||||||
return Optional.ofNullable(pages.get(++pageIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
boolean canAdvance(WizardPage currentPage) {
|
|
||||||
int pageIndex = pages.indexOf(currentPage);
|
|
||||||
return pages.size() - 1 > pageIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,206 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2014, 2015 ControlsFX
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* MODIFIED BY DORKBOX
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.javafx;
|
|
||||||
|
|
||||||
import dorkbox.util.JavaFxUtil;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Control;
|
|
||||||
import javafx.scene.control.DialogPane;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.text.Font;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import org.controlsfx.validation.ValidationMessage;
|
|
||||||
import org.controlsfx.validation.ValidationResult;
|
|
||||||
import org.controlsfx.validation.ValidationSupport;
|
|
||||||
import org.controlsfx.validation.Validator;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WizardPane is the base class for all wizard pages. The API is essentially the {@link DialogPane}, with the addition of convenience
|
|
||||||
* methods related to {@link #onEnteringPage(Wizard) entering} and {@link #onExitingPage(Wizard) exiting} the page.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("UnusedParameters")
|
|
||||||
public
|
|
||||||
class WizardPage {
|
|
||||||
|
|
||||||
String headerText;
|
|
||||||
Font headerFont;
|
|
||||||
Node headerGraphic;
|
|
||||||
|
|
||||||
Node anchorPane;
|
|
||||||
|
|
||||||
Node firstFocusElement;
|
|
||||||
|
|
||||||
final StringProperty invalidPropertyStrings = new SimpleStringProperty();
|
|
||||||
final BooleanProperty invalidProperty = new SimpleBooleanProperty();
|
|
||||||
final ValidationSupport validationSupport = new ValidationSupport();
|
|
||||||
boolean autoFocusNext = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of wizard pane.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
WizardPage() {
|
|
||||||
validationSupport.validationResultProperty()
|
|
||||||
.addListener((ObservableValue<? extends ValidationResult> o, ValidationResult ov, ValidationResult nv) -> {
|
|
||||||
final Collection<ValidationMessage> errors = nv.getErrors();
|
|
||||||
|
|
||||||
final boolean empty = errors.isEmpty();
|
|
||||||
if (empty) {
|
|
||||||
invalidPropertyStrings.set(null);
|
|
||||||
} else {
|
|
||||||
String errorText = errors.iterator()
|
|
||||||
.next()
|
|
||||||
.getText()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
if (!errorText.equals(invalidPropertyStrings.get())) {
|
|
||||||
invalidPropertyStrings.set(errorText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invalidProperty.set(!empty);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on entering a page. This is a good place to read values from wizard settings and assign them to controls on the page
|
|
||||||
*
|
|
||||||
* @param wizard
|
|
||||||
* which page will be used on
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void onEnteringPage(Wizard wizard) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on existing the page. This is a good place to read values from page controls and store them in wizard settings
|
|
||||||
*
|
|
||||||
* @param wizard
|
|
||||||
* which page was used on
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void onExitingPage(Wizard wizard) {
|
|
||||||
// no-opd
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setHeaderText(final String headerText) {
|
|
||||||
this.headerText = headerText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setContent(final Node content) {
|
|
||||||
// make this content fill the parent (which is a vbox)
|
|
||||||
VBox.setVgrow(content, Priority.ALWAYS);
|
|
||||||
this.anchorPane = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setContentText(final String contentText) {
|
|
||||||
Text text = new Text();
|
|
||||||
text.setFont(JavaFxUtil.DEFAULT_FONT);
|
|
||||||
text.setText(contentText);
|
|
||||||
|
|
||||||
// we use a Vbox, so that the text starts at the topleft.
|
|
||||||
// if we used a stackpane, it would be centered in the parent node
|
|
||||||
VBox region = new VBox();
|
|
||||||
region.setPadding(new Insets(6));
|
|
||||||
|
|
||||||
region.getChildren().add(text);
|
|
||||||
region.setMinSize(0, 0);
|
|
||||||
|
|
||||||
// make this content fill the parent (which is a vbox)
|
|
||||||
VBox.setVgrow(region, Priority.ALWAYS);
|
|
||||||
this.anchorPane = region;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
void setFirstFocusElement(final Node firstFocusElement) {
|
|
||||||
this.firstFocusElement = firstFocusElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
void setHeaderFont(final Font headerFont) {
|
|
||||||
this.headerFont = headerFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
void registerValidator(final Control control, final Validator<Object> validator) {
|
|
||||||
this.validationSupport.registerValidator(control, validator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Necessary for enabling the "Next/Finish" button when a task is complete.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void bind(final ObservableValue<Boolean> invalidProperty) {
|
|
||||||
this.invalidProperty.bind(invalidProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables the wizard to automatically focus on the "Next" (or "Finish"), when this item is valid.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void autoFocusNext() {
|
|
||||||
autoFocusNext = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void setHeaderGraphic(final Node headerGraphic) {
|
|
||||||
this.headerGraphic = headerGraphic;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,325 +0,0 @@
|
||||||
|
|
||||||
package dorkbox.util.crypto;
|
|
||||||
|
|
||||||
import dorkbox.util.crypto.bouncycastle.GCMBlockCipher_ByteBuf;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import org.bouncycastle.crypto.engines.AESFastEngine;
|
|
||||||
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
|
||||||
import org.bouncycastle.crypto.params.KeyParameter;
|
|
||||||
import org.bouncycastle.crypto.params.ParametersWithIV;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public class AesByteBufTest {
|
|
||||||
|
|
||||||
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
|
|
||||||
private static String entropySeed = "asdjhasdkljalksdfhlaks4356268909087s0dfgkjh255124515hasdg87";
|
|
||||||
|
|
||||||
private String text = "hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya." +
|
|
||||||
"hello, my name is inigo montoya. hello, my name is inigo montoya. hello, my name is inigo montoya.";
|
|
||||||
|
|
||||||
// test input smaller than block size
|
|
||||||
private byte[] bytes = "hello!".getBytes();
|
|
||||||
|
|
||||||
// test input larger than block size
|
|
||||||
private byte[] bytesLarge = text.getBytes();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmEncryptBothA() throws IOException {
|
|
||||||
|
|
||||||
final byte[] SOURCE = bytesLarge;
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
final GCMBlockCipher_ByteBuf aesEngine1 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
final GCMBlockCipher aesEngine2 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
|
|
||||||
final byte[] key = new byte[32];
|
|
||||||
final byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
final ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
|
|
||||||
ByteBuf source = Unpooled.wrappedBuffer(SOURCE);
|
|
||||||
int length = SOURCE.length;
|
|
||||||
ByteBuf encryptAES = Unpooled.buffer(1024);
|
|
||||||
int encryptLength = CryptoAES.encrypt(aesEngine1, aesIVAndKey, source, encryptAES, length, logger);
|
|
||||||
|
|
||||||
byte[] encrypt = new byte[encryptLength];
|
|
||||||
System.arraycopy(encryptAES.array(), 0, encrypt, 0, encryptLength);
|
|
||||||
|
|
||||||
byte[] encrypt2 = CryptoAES.encrypt(aesEngine2, key, iv, SOURCE, logger);
|
|
||||||
|
|
||||||
|
|
||||||
if (Arrays.equals(SOURCE, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(encrypt, encrypt2)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmEncryptBothB() throws IOException {
|
|
||||||
final byte[] SOURCE = bytes;
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
final GCMBlockCipher_ByteBuf aesEngine1 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
final GCMBlockCipher aesEngine2 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
|
|
||||||
final byte[] key = new byte[32];
|
|
||||||
final byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
final ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
|
|
||||||
ByteBuf source = Unpooled.wrappedBuffer(SOURCE);
|
|
||||||
int length = SOURCE.length;
|
|
||||||
ByteBuf encryptAES = Unpooled.buffer(1024);
|
|
||||||
int encryptLength = CryptoAES.encrypt(aesEngine1, aesIVAndKey, source, encryptAES, length, logger);
|
|
||||||
|
|
||||||
byte[] encrypt = new byte[encryptLength];
|
|
||||||
System.arraycopy(encryptAES.array(), 0, encrypt, 0, encryptLength);
|
|
||||||
|
|
||||||
|
|
||||||
byte[] encrypt2 = CryptoAES.encrypt(aesEngine2, key, iv, SOURCE, logger);
|
|
||||||
|
|
||||||
|
|
||||||
if (Arrays.equals(SOURCE, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(encrypt, encrypt2)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmEncryptBufOnly() throws IOException {
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
GCMBlockCipher_ByteBuf aesEngine1 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
GCMBlockCipher aesEngine2 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
|
|
||||||
byte[] key = new byte[32];
|
|
||||||
byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
ByteBuf source = Unpooled.wrappedBuffer(bytes);
|
|
||||||
int length = bytes.length;
|
|
||||||
ByteBuf encryptAES = Unpooled.buffer(1024);
|
|
||||||
int encryptLength = CryptoAES.encrypt(aesEngine1, aesIVAndKey, source, encryptAES, length, logger);
|
|
||||||
|
|
||||||
byte[] encrypt = new byte[encryptLength];
|
|
||||||
System.arraycopy(encryptAES.array(), 0, encrypt, 0, encryptLength);
|
|
||||||
|
|
||||||
|
|
||||||
byte[] decrypt = CryptoAES.decrypt(aesEngine2, key, iv, encrypt, logger);
|
|
||||||
|
|
||||||
|
|
||||||
if (Arrays.equals(bytes, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(bytes, decrypt)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmDecryptBothA() throws IOException {
|
|
||||||
|
|
||||||
final byte[] SOURCE = bytesLarge;
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
final GCMBlockCipher aesEngine1 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
final GCMBlockCipher_ByteBuf aesEngine2 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
|
|
||||||
final byte[] key = new byte[32];
|
|
||||||
final byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
final byte[] encrypt = CryptoAES.encrypt(aesEngine1, key, iv, SOURCE, logger);
|
|
||||||
final ByteBuf encryptAES = Unpooled.wrappedBuffer(encrypt);
|
|
||||||
final int length = encrypt.length;
|
|
||||||
|
|
||||||
byte[] decrypt1 = CryptoAES.decrypt(aesEngine1, key, iv, encrypt, logger);
|
|
||||||
|
|
||||||
|
|
||||||
ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
ByteBuf decryptAES = Unpooled.buffer(1024);
|
|
||||||
int decryptLength = CryptoAES.decrypt(aesEngine2, aesIVAndKey, encryptAES, decryptAES, length, logger);
|
|
||||||
byte[] decrypt2 = new byte[decryptLength];
|
|
||||||
System.arraycopy(decryptAES.array(), 0, decrypt2, 0, decryptLength);
|
|
||||||
|
|
||||||
|
|
||||||
if (Arrays.equals(SOURCE, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(decrypt1, decrypt2)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(SOURCE, decrypt1)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmDecryptBothB() throws IOException {
|
|
||||||
|
|
||||||
byte[] SOURCE = bytes;
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
final GCMBlockCipher aesEngine1 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
final GCMBlockCipher_ByteBuf aesEngine2 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
|
|
||||||
final byte[] key = new byte[32];
|
|
||||||
final byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
final byte[] encrypt = CryptoAES.encrypt(aesEngine1, key, iv, SOURCE, logger);
|
|
||||||
final ByteBuf encryptAES = Unpooled.wrappedBuffer(encrypt);
|
|
||||||
final int length = encrypt.length;
|
|
||||||
|
|
||||||
|
|
||||||
byte[] decrypt1 = CryptoAES.decrypt(aesEngine1, key, iv, encrypt, logger);
|
|
||||||
|
|
||||||
|
|
||||||
ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
ByteBuf decryptAES = Unpooled.buffer(1024);
|
|
||||||
int decryptLength = CryptoAES.decrypt(aesEngine2, aesIVAndKey, encryptAES, decryptAES, length, logger);
|
|
||||||
byte[] decrypt2 = new byte[decryptLength];
|
|
||||||
System.arraycopy(decryptAES.array(), 0, decrypt2, 0, decryptLength);
|
|
||||||
|
|
||||||
|
|
||||||
if (Arrays.equals(SOURCE, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(decrypt1, decrypt2)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(SOURCE, decrypt1)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmDecryptBufOnlyA() throws IOException {
|
|
||||||
byte[] SOURCE = bytesLarge;
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
GCMBlockCipher aesEngine1 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
GCMBlockCipher_ByteBuf aesEngine2 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
|
|
||||||
byte[] key = new byte[32];
|
|
||||||
byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
|
|
||||||
byte[] encrypt = CryptoAES.encrypt(aesEngine1, key, iv, SOURCE, logger);
|
|
||||||
ByteBuf encryptAES = Unpooled.wrappedBuffer(encrypt);
|
|
||||||
int length = encrypt.length;
|
|
||||||
|
|
||||||
|
|
||||||
ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
ByteBuf decryptAES = Unpooled.buffer(1024);
|
|
||||||
int decryptLength = CryptoAES.decrypt(aesEngine2, aesIVAndKey, encryptAES, decryptAES, length, logger);
|
|
||||||
byte[] decrypt = new byte[decryptLength];
|
|
||||||
System.arraycopy(decryptAES.array(), 0, decrypt, 0, decryptLength);
|
|
||||||
|
|
||||||
if (Arrays.equals(SOURCE, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(SOURCE, decrypt)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AesGcmDecryptBufOnlyB() throws IOException {
|
|
||||||
byte[] SOURCE = bytes;
|
|
||||||
|
|
||||||
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
|
|
||||||
|
|
||||||
GCMBlockCipher aesEngine1 = new GCMBlockCipher(new AESFastEngine());
|
|
||||||
GCMBlockCipher_ByteBuf aesEngine2 = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
|
||||||
|
|
||||||
byte[] key = new byte[32];
|
|
||||||
byte[] iv = new byte[16];
|
|
||||||
|
|
||||||
// note: the IV needs to be VERY unique!
|
|
||||||
rand.nextBytes(key); // 256bit key (32 bytes)
|
|
||||||
rand.nextBytes(iv); // 128bit block size (16 bytes)
|
|
||||||
|
|
||||||
|
|
||||||
byte[] encrypt = CryptoAES.encrypt(aesEngine1, key, iv, SOURCE, logger);
|
|
||||||
ByteBuf encryptAES = Unpooled.wrappedBuffer(encrypt);
|
|
||||||
int length = encrypt.length;
|
|
||||||
|
|
||||||
|
|
||||||
ParametersWithIV aesIVAndKey = new ParametersWithIV(new KeyParameter(key), iv);
|
|
||||||
ByteBuf decryptAES = Unpooled.buffer(1024);
|
|
||||||
int decryptLength = CryptoAES.decrypt(aesEngine2, aesIVAndKey, encryptAES, decryptAES, length, logger);
|
|
||||||
byte[] decrypt = new byte[decryptLength];
|
|
||||||
System.arraycopy(decryptAES.array(), 0, decrypt, 0, decryptLength);
|
|
||||||
|
|
||||||
if (Arrays.equals(SOURCE, encrypt)) {
|
|
||||||
fail("bytes should not be equal");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(SOURCE, decrypt)) {
|
|
||||||
fail("bytes not equal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user