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 javafx.scene.control.skin.AccordionSkin;
  32 import com.sun.javafx.scene.control.skin.resources.ControlResources;
  33 import hello.HelloAccordion;
  34 import javafx.beans.binding.DoubleBinding;
  35 import javafx.collections.ListChangeListener;
  36 import javafx.event.ActionEvent;
  37 import javafx.event.EventHandler;
  38 import javafx.geometry.Insets;
  39 import javafx.geometry.Pos;
  40 import javafx.geometry.VPos;
  41 import javafx.scene.Node;
  42 import javafx.scene.control.Button;
  43 import javafx.scene.control.ButtonBar.ButtonData;
  44 import javafx.scene.control.ButtonType;
  45 import javafx.scene.control.Dialog;
  46 import javafx.scene.control.DialogPane;
  47 import javafx.scene.control.Label;
  48 import javafx.scene.image.Image;
  49 import javafx.scene.image.ImageView;
  50 import javafx.scene.layout.GridPane;
  51 import javafx.scene.layout.Priority;
  52 
  53 public class CommandLinksDialog extends Dialog<ButtonType> {
  54     
  55     private final static int gapSize = 10;
  56     
  57     private final List<Button> buttons = new ArrayList<>();
  58     
  59     private GridPane grid = new GridPane() {
  60         @Override protected double computePrefWidth(double height) {
  61             double pw = 0;
  62 
  63             for (int i = 0; i < buttons.size(); i++) {
  64                 Button btn = buttons.get(i);
  65                 pw = Math.min(pw, btn.prefWidth(-1));
  66             }
  67             return pw + gapSize;
  68         }
  69 
  70         @Override protected double computePrefHeight(double width) {
  71             double ph = getDialogPane().getHeader() == null ? 0 : 10;
  72 
  73             for (int i = 0; i < buttons.size(); i++) {
  74                 Button btn = buttons.get(i);
  75                 ph += btn.prefHeight(width) + gapSize;
  76             }
  77             return ph * 1.5;
  78         }
  79     };
  80     
  81     public CommandLinksDialog(ButtonType... links) {
  82         this(Arrays.asList(links));
  83     }
  84     
  85     public CommandLinksDialog(List<ButtonType> links) {
  86         this.grid.setHgap(gapSize);
  87         this.grid.setVgap(gapSize);
  88         
  89         final DialogPane dialogPane = new DialogPane() {
  90             @Override protected Node createButtonBar() {
  91                 return null;
  92             }
  93         }; 
  94         setDialogPane(dialogPane);
  95         
  96         dialogPane.getStylesheets().add(getClass().getResource("commandlink.css").toExternalForm());
  97 
  98         setTitle(ControlResources.getString("Dialog.info.title"));
  99 
 100         // FIXME extract to CSS
 101         dialogPane.setGraphic(new ImageView(new Image(AccordionSkin.class.getResource("modena/dialog-information.png").toExternalForm())));
 102         dialogPane.getButtonTypes().addAll(links);
 103         
 104         dialogPane.contentProperty().addListener(o -> updateGrid());
 105 
 106         updateGrid();
 107         dialogPane.getButtonTypes().addListener((ListChangeListener<? super ButtonType>)c -> {
 108             updateGrid();
 109         });
 110     }
 111     
 112     private void updateGrid() {
 113         final Node content = getDialogPane().getContent();
 114         final boolean dialogContentIsGrid = grid == content;
 115         
 116         if (! dialogContentIsGrid) {
 117             if (content != null) {
 118                 content.getStyleClass().add("command-link-message");
 119                 grid.add(content, 0, 0);
 120             }
 121         }
 122         
 123         grid.getChildren().removeAll(buttons);
 124         int row = 1;
 125         for (final ButtonType action : getDialogPane().getButtonTypes()) {
 126             if (action == null) continue; 
 127             
 128 //            if (! (action instanceof DialogButton)) {
 129 //                throw new IllegalArgumentException("All actions in CommandLinksDialog must be instances of DialogAction");
 130 //            }
 131 //            
 132 //            DialogButton commandLink = (DialogButton) action;
 133 
 134 //            //replace link's event handler with a proper one
 135 //            commandLink.setOnAction(new EventHandler<ActionEvent>() {
 136 //                @Override public void handle(ActionEvent ae) {
 137 //                    setResult(commandLink);
 138 //                }
 139 //            });
 140 
 141             final Button button = buildCommandLinkButton(action);   
 142             final ButtonData buttonType = action.getButtonData();
 143             button.setDefaultButton(buttonType != null && buttonType.isDefaultButton());
 144 //            button.setOnAction(commandLink);
 145             
 146             button.setOnAction(new EventHandler<ActionEvent>() {
 147                 @Override public void handle(ActionEvent ae) {
 148                     setResult(action);
 149                 }
 150             });
 151 
 152             GridPane.setHgrow(button, Priority.ALWAYS);
 153             GridPane.setVgrow(button, Priority.ALWAYS);
 154             grid.add(button, 0, row++);
 155             buttons.add(button);
 156         }
 157 
 158         // last button gets some extra padding (hacky)
 159         GridPane.setMargin(buttons.get(buttons.size() - 1), new Insets(0,0,10,0));
 160 
 161         if (! dialogContentIsGrid) {
 162             getDialogPane().setContent(grid);
 163         }
 164     }
 165     
 166     private Button buildCommandLinkButton(ButtonType commandLink) {
 167         // put the content inside a button
 168         final Button button = new Button();
 169         button.getStyleClass().addAll("command-link-button");
 170         button.setMaxHeight(Double.MAX_VALUE);
 171         button.setMaxWidth(Double.MAX_VALUE);
 172         button.setAlignment(Pos.CENTER_LEFT);
 173 
 174         final Label titleLabel = new Label(commandLink.getText() );
 175         titleLabel.minWidthProperty().bind(new DoubleBinding() {
 176             {
 177                 bind(titleLabel.prefWidthProperty());
 178             }
 179 
 180             @Override protected double computeValue() {
 181                 return titleLabel.getPrefWidth() + 400;
 182             }
 183         });
 184         titleLabel.getStyleClass().addAll("line-1");
 185         titleLabel.setWrapText(true);
 186         titleLabel.setAlignment(Pos.TOP_LEFT);
 187         GridPane.setVgrow(titleLabel, Priority.NEVER);
 188 
 189         // TODO no support in DialogButton for long text or graphic
 190 //        Label messageLabel = new Label(commandLink.getLongText() );
 191 //        messageLabel.getStyleClass().addAll("line-2");
 192 //        messageLabel.setWrapText(true);
 193 //        messageLabel.setAlignment(Pos.TOP_LEFT);
 194 //        messageLabel.setMaxHeight(Double.MAX_VALUE);
 195 //        GridPane.setVgrow(messageLabel, Priority.SOMETIMES);
 196 //
 197 //        Image commandLinkImage = commandLink.getGraphic();
 198 //        Node view = commandLinkImage == null ? 
 199 //                new ImageView(getClass().getResource("arrow-green-right.png").toExternalForm()) : 
 200 //                new ImageView(commandLinkImage);
 201 //        Pane graphicContainer = new Pane(view);
 202 //        graphicContainer.getStyleClass().add("graphic-container");
 203         ImageView arrow = new ImageView(HelloAccordion.class.getResource("about_16.png").toExternalForm());
 204         GridPane.setValignment(arrow, VPos.TOP);
 205         GridPane.setMargin(arrow, new Insets(0,10,0,0));
 206 
 207         GridPane grid = new GridPane();
 208         grid.minWidthProperty().bind(titleLabel.prefWidthProperty());
 209         grid.setMaxHeight(Double.MAX_VALUE);
 210         grid.setMaxWidth(Double.MAX_VALUE);
 211         grid.getStyleClass().add("container");
 212         grid.add(arrow, 0, 0, 1, 2);
 213         grid.add(titleLabel, 1, 0);
 214 //        grid.add(messageLabel, 1, 1);
 215 
 216         button.setGraphic(grid);
 217         button.minWidthProperty().bind(titleLabel.prefWidthProperty());
 218 
 219         return button;
 220     }    
 221 }