1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package hello.dialog.dialogs;
  26 
  27 import java.util.ArrayList;
  28 import java.util.Arrays;
  29 import java.util.List;
  30 
  31 import com.sun.javafx.scene.control.skin.resources.ControlResources;
  32 import hello.HelloAccordion;
  33 import javafx.beans.binding.DoubleBinding;
  34 import javafx.collections.ListChangeListener;
  35 import javafx.event.ActionEvent;
  36 import javafx.event.EventHandler;
  37 import javafx.geometry.Insets;
  38 import javafx.geometry.Pos;
  39 import javafx.geometry.VPos;
  40 import javafx.scene.Node;
  41 import javafx.scene.control.Button;
  42 import javafx.scene.control.ButtonBar.ButtonData;
  43 import javafx.scene.control.ButtonType;
  44 import javafx.scene.control.Dialog;
  45 import javafx.scene.control.DialogPane;
  46 import javafx.scene.control.Label;
  47 import javafx.scene.image.Image;
  48 import javafx.scene.image.ImageView;
  49 import javafx.scene.layout.GridPane;
  50 import javafx.scene.layout.Priority;
  51 
  52 public class CommandLinksDialog extends Dialog<ButtonType> {
  53     
  54     private final static int gapSize = 10;
  55     
  56     private final List<Button> buttons = new ArrayList<>();
  57     
  58     private GridPane grid = new GridPane() {
  59         @Override protected double computePrefWidth(double height) {
  60             double pw = 0;
  61 
  62             for (int i = 0; i < buttons.size(); i++) {
  63                 Button btn = buttons.get(i);
  64                 pw = Math.min(pw, btn.prefWidth(-1));
  65             }
  66             return pw + gapSize;
  67         }
  68 
  69         @Override protected double computePrefHeight(double width) {
  70             double ph = getDialogPane().getHeader() == null ? 0 : 10;
  71 
  72             for (int i = 0; i < buttons.size(); i++) {
  73                 Button btn = buttons.get(i);
  74                 ph += btn.prefHeight(width) + gapSize;
  75             }
  76             return ph * 1.5;
  77         }
  78     };
  79     
  80     public CommandLinksDialog(ButtonType... links) {
  81         this(Arrays.asList(links));
  82     }
  83     
  84     public CommandLinksDialog(List<ButtonType> links) {
  85         this.grid.setHgap(gapSize);
  86         this.grid.setVgap(gapSize);
  87         
  88         final DialogPane dialogPane = new DialogPane() {
  89             @Override protected Node createButtonBar() {
  90                 return null;
  91             }
  92         }; 
  93         setDialogPane(dialogPane);
  94         
  95         dialogPane.getStylesheets().add(getClass().getResource("commandlink.css").toExternalForm());
  96 
  97         setTitle(ControlResources.getString("Dialog.info.title"));
  98 
  99         // FIXME extract to CSS
 100         dialogPane.setGraphic(new ImageView(new Image(HelloAccordion.class.getResource("dialog/dialog-information.png").toExternalForm())));
 101         dialogPane.getButtonTypes().addAll(links);
 102         
 103         dialogPane.contentProperty().addListener(o -> updateGrid());
 104 
 105         updateGrid();
 106         dialogPane.getButtonTypes().addListener((ListChangeListener<? super ButtonType>)c -> {
 107             updateGrid();
 108         });
 109     }
 110     
 111     private void updateGrid() {
 112         final Node content = getDialogPane().getContent();
 113         final boolean dialogContentIsGrid = grid == content;
 114         
 115         if (! dialogContentIsGrid) {
 116             if (content != null) {
 117                 content.getStyleClass().add("command-link-message");
 118                 grid.add(content, 0, 0);
 119             }
 120         }
 121         
 122         grid.getChildren().removeAll(buttons);
 123         int row = 1;
 124         for (final ButtonType action : getDialogPane().getButtonTypes()) {
 125             if (action == null) continue; 
 126             
 127 //            if (! (action instanceof DialogButton)) {
 128 //                throw new IllegalArgumentException("All actions in CommandLinksDialog must be instances of DialogAction");
 129 //            }
 130 //            
 131 //            DialogButton commandLink = (DialogButton) action;
 132 
 133 //            //replace link's event handler with a proper one
 134 //            commandLink.setOnAction(new EventHandler<ActionEvent>() {
 135 //                @Override public void handle(ActionEvent ae) {
 136 //                    setResult(commandLink);
 137 //                }
 138 //            });
 139 
 140             final Button button = buildCommandLinkButton(action);   
 141             final ButtonData buttonType = action.getButtonData();
 142             button.setDefaultButton(buttonType != null && buttonType.isDefaultButton());
 143 //            button.setOnAction(commandLink);
 144             
 145             button.setOnAction(new EventHandler<ActionEvent>() {
 146                 @Override public void handle(ActionEvent ae) {
 147                     setResult(action);
 148                 }
 149             });
 150 
 151             GridPane.setHgrow(button, Priority.ALWAYS);
 152             GridPane.setVgrow(button, Priority.ALWAYS);
 153             grid.add(button, 0, row++);
 154             buttons.add(button);
 155         }
 156 
 157         // last button gets some extra padding (hacky)
 158         GridPane.setMargin(buttons.get(buttons.size() - 1), new Insets(0,0,10,0));
 159 
 160         if (! dialogContentIsGrid) {
 161             getDialogPane().setContent(grid);
 162         }
 163     }
 164     
 165     private Button buildCommandLinkButton(ButtonType commandLink) {
 166         // put the content inside a button
 167         final Button button = new Button();
 168         button.getStyleClass().addAll("command-link-button");
 169         button.setMaxHeight(Double.MAX_VALUE);
 170         button.setMaxWidth(Double.MAX_VALUE);
 171         button.setAlignment(Pos.CENTER_LEFT);
 172 
 173         final Label titleLabel = new Label(commandLink.getText() );
 174         titleLabel.minWidthProperty().bind(new DoubleBinding() {
 175             {
 176                 bind(titleLabel.prefWidthProperty());
 177             }
 178 
 179             @Override protected double computeValue() {
 180                 return titleLabel.getPrefWidth() + 400;
 181             }
 182         });
 183         titleLabel.getStyleClass().addAll("line-1");
 184         titleLabel.setWrapText(true);
 185         titleLabel.setAlignment(Pos.TOP_LEFT);
 186         GridPane.setVgrow(titleLabel, Priority.NEVER);
 187 
 188         // TODO no support in DialogButton for long text or graphic
 189 //        Label messageLabel = new Label(commandLink.getLongText() );
 190 //        messageLabel.getStyleClass().addAll("line-2");
 191 //        messageLabel.setWrapText(true);
 192 //        messageLabel.setAlignment(Pos.TOP_LEFT);
 193 //        messageLabel.setMaxHeight(Double.MAX_VALUE);
 194 //        GridPane.setVgrow(messageLabel, Priority.SOMETIMES);
 195 //
 196 //        Image commandLinkImage = commandLink.getGraphic();
 197 //        Node view = commandLinkImage == null ? 
 198 //                new ImageView(getClass().getResource("arrow-green-right.png").toExternalForm()) : 
 199 //                new ImageView(commandLinkImage);
 200 //        Pane graphicContainer = new Pane(view);
 201 //        graphicContainer.getStyleClass().add("graphic-container");
 202         ImageView arrow = new ImageView(HelloAccordion.class.getResource("about_16.png").toExternalForm());
 203         GridPane.setValignment(arrow, VPos.TOP);
 204         GridPane.setMargin(arrow, new Insets(0,10,0,0));
 205 
 206         GridPane grid = new GridPane();
 207         grid.minWidthProperty().bind(titleLabel.prefWidthProperty());
 208         grid.setMaxHeight(Double.MAX_VALUE);
 209         grid.setMaxWidth(Double.MAX_VALUE);
 210         grid.getStyleClass().add("container");
 211         grid.add(arrow, 0, 0, 1, 2);
 212         grid.add(titleLabel, 1, 0);
 213 //        grid.add(messageLabel, 1, 1);
 214 
 215         button.setGraphic(grid);
 216         button.minWidthProperty().bind(titleLabel.prefWidthProperty());
 217 
 218         return button;
 219     }    
 220 }