/***********************************************************************************
*
* Copyright (c) 2013-2015 ControlsFX, Kamil Baczkowicz
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the BSD 3-Clause License which
* accompany this distribution.
*
* The BSD 3-Clause License is available at
* http://opensource.org/licenses/BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
*
* ControlsFX - initial implementation
* Kamil Baczkowicz - minor changes, derivative work created from ControlsFX (http://fxexperience.com/controlsfx/)
*
*/
package pl.baczkowicz.spy.ui.controls;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javafx.beans.binding.DoubleBinding;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.text.TextAlignment;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
import pl.baczkowicz.spy.ui.utils.ImageUtils;
public class CommandLinksDialog
{
/** Diagnostic logger. */
// private final static Logger logger = LoggerFactory.getLogger(CommandLinksDialog.class);
/**
* Show a dialog filled with provided command links. Command links are used instead of button bar and represent
* a set of available 'radio' buttons
* @param message
* @param string
* @param defaultCommandLink command is set to be default. Null means no default
* @param links list of command links presented in specified sequence
* @return action used to close dialog (it is either one of command links or CANCEL)
*/
public static Optional<DialogAction> showCommandLinks(final String title, final String message,
final DialogAction defaultCommandLink, final List<DialogAction> links,
final double windowHeight, final int minWidth, final int longMessageMinHeight, double maxHeight, final List<String> stylesheets)
{
final Dialog<DialogAction> dialog = new Dialog<DialogAction>();
dialog.setTitle(title);
dialog.getDialogPane().getScene().getStylesheets().addAll(stylesheets);
dialog.getDialogPane().getButtonTypes().clear();
dialog.setGraphic(ImageUtils.createIcon("dialog-information-large", 55));
dialog.setResizable(true);
dialog.setHeight(windowHeight);
Label label = new Label(message);
label.setAlignment(Pos.TOP_LEFT);
label.setTextAlignment(TextAlignment.LEFT);
label.setMaxWidth(Double.MAX_VALUE);
label.setMaxHeight(Double.MAX_VALUE);
label.setWrapText(true);
label.getStyleClass().add("command-link-message");
final int gapSize = 10;
final List<Button> buttons = new ArrayList<>(links.size());
GridPane content = new GridPane()
{
@Override
protected double computePrefWidth(double height)
{
double pw = 0;
for (int i = 0; i < buttons.size(); i++)
{
Button btn = buttons.get(i);
pw = Math.min(pw, btn.prefWidth(-1));
}
return pw + gapSize;
}
@Override
protected double computePrefHeight(double width)
{
double ph = 10;
for (int i = 0; i < buttons.size(); i++)
{
Button btn = buttons.get(i);
ph += btn.prefHeight(width) + gapSize;
}
return ph * 1.5;
}
};
int row = 0;
content.add(label, 0, row++);
content.setMinWidth(minWidth);
content.setMaxHeight(windowHeight);
content.setPrefHeight(windowHeight);
content.setHgap(gapSize);
content.setVgap(gapSize);
for (final DialogAction commandLink : links)
{
if (commandLink == null)
{
continue;
}
final Button button = buildCommandLinkButton(commandLink, longMessageMinHeight, maxHeight);
button.setDefaultButton(commandLink == defaultCommandLink);
button.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent ae)
{
dialog.setResult(commandLink);
}
});
commandLink.setButton(button);
GridPane.setHgrow(button, Priority.ALWAYS);
GridPane.setVgrow(button, Priority.ALWAYS);
content.add(button, 0, row++);
buttons.add(button);
}
// last button gets some extra padding (hacky)
GridPane.setMargin(buttons.get(buttons.size() - 1), new Insets(0,0,10,0));
dialog.getDialogPane().setContent(content);
ButtonType cancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().add(cancel);
return dialog.showAndWait();
}
private static Button buildCommandLinkButton(DialogAction commandLink, final int longMessageMinHeight, double maxHeight)
{
// put the content inside a button
final Button button = new Button();
button.getStyleClass().addAll("command-link-button");
button.setMaxHeight(maxHeight);
button.setMaxWidth(Double.MAX_VALUE);
button.setAlignment(Pos.CENTER_LEFT);
final Label titleLabel = new Label(commandLink.getHeading() );
titleLabel.minWidthProperty().bind(new DoubleBinding()
{
{
bind(titleLabel.prefWidthProperty());
}
@Override protected double computeValue() {
return titleLabel.getPrefWidth() + 400;
}
});
titleLabel.getStyleClass().addAll("line-1");
titleLabel.setWrapText(true);
titleLabel.setAlignment(Pos.TOP_LEFT);
GridPane.setVgrow(titleLabel, Priority.NEVER);
Label messageLabel = new Label(commandLink.getLongText() );
messageLabel.setMinHeight(longMessageMinHeight);
messageLabel.setPrefHeight(longMessageMinHeight + 10);
//messageLabel.setMaxHeight(longMessageMaxHeight);
messageLabel.getStyleClass().addAll("line-2");
messageLabel.setWrapText(true);
messageLabel.setAlignment(Pos.TOP_LEFT);
messageLabel.setMaxHeight(Double.MAX_VALUE);
// GridPane.setVgrow(messageLabel, Priority.SOMETIMES);
GridPane.setVgrow(messageLabel, Priority.ALWAYS);
Pane graphicContainer = new Pane(ImageUtils.createIcon("go-next-green", 20));
graphicContainer.getStyleClass().add("graphic-container");
GridPane.setValignment(graphicContainer, VPos.TOP);
GridPane.setMargin(graphicContainer, new Insets(0,15,0,0));
GridPane grid = new GridPane();
grid.minWidthProperty().bind(titleLabel.prefWidthProperty());
grid.setMaxHeight(Double.MAX_VALUE);
grid.setMaxWidth(Double.MAX_VALUE);
grid.getStyleClass().add("container");
grid.add(graphicContainer, 0, 0, 1, 2);
grid.add(titleLabel, 1, 0);
grid.add(messageLabel, 1, 1);
button.setGraphic(grid);
button.minWidthProperty().bind(titleLabel.prefWidthProperty());
return button;
}
}