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 javafx.scene.control;
  26 
  27 import java.util.Arrays;
  28 import java.util.Collection;
  29 import java.util.Collections;
  30 
  31 import com.sun.javafx.scene.control.skin.resources.ControlResources;
  32 import javafx.application.Platform;
  33 import javafx.beans.property.ReadOnlyObjectProperty;
  34 import javafx.collections.ObservableList;
  35 import javafx.geometry.Pos;
  36 import javafx.scene.control.ButtonBar.ButtonData;
  37 import javafx.scene.layout.GridPane;
  38 import javafx.scene.layout.Priority;
  39 import javafx.scene.layout.Region;
  40 
  41 /**
  42  * A dialog that shows a list of choices to the user, from which they can pick
  43  * one item at most.
  44  *
  45  * @see Dialog
  46  * @param <T> The type of the items to show to the user, and the type that is returned
  47  *            via {@link #getResult()} when the dialog is dismissed.
  48  * @since JavaFX 8u40
  49  */
  50 public class ChoiceDialog<T> extends Dialog<T> {
  51 
  52     /**************************************************************************
  53      *
  54      * Fields
  55      *
  56      **************************************************************************/
  57 
  58     private final GridPane grid;
  59     private final Label label;
  60     private final ComboBox<T> comboBox;
  61     private final T defaultChoice;
  62 
  63 
  64 
  65     /**************************************************************************
  66      *
  67      * Constructors
  68      *
  69      **************************************************************************/
  70 
  71     /**
  72      * Creates a default, empty instance of ChoiceDialog with no set items and a
  73      * null default choice. Users of this constructor will subsequently need to
  74      * call {@link #getItems()} to specify which items to show to the user.
  75      */
  76     public ChoiceDialog() {
  77         this((T)null, (T[])null);
  78     }
  79 
  80     /**
  81      * Creates a new ChoiceDialog instance with the first argument specifying the
  82      * default choice that should be shown to the user, and all following arguments
  83      * considered a varargs array of all available choices for the user. It is
  84      * expected that the defaultChoice be one of the elements in the choices varargs
  85      * array. If this is not true, then defaultChoice will be set to null and the
  86      * dialog will show with the initial choice set to the first item in the list
  87      * of choices.
  88      *
  89      * @param defaultChoice The item to display as the pre-selected choice in the dialog.
  90      *        This item must be contained within the choices varargs array.
  91      * @param choices All possible choices to present to the user.
  92      */
  93     public ChoiceDialog(T defaultChoice,  @SuppressWarnings("unchecked") T... choices) {
  94         this(defaultChoice,
  95              choices == null ? Collections.emptyList() : Arrays.asList(choices));
  96     }
  97 
  98     /**
  99      * Creates a new ChoiceDialog instance with the first argument specifying the
 100      * default choice that should be shown to the user, and the second argument
 101      * specifying a collection of all available choices for the user. It is
 102      * expected that the defaultChoice be one of the elements in the choices
 103      * collection. If this is not true, then defaultChoice will be set to null and the
 104      * dialog will show with the initial choice set to the first item in the list
 105      * of choices.
 106      *
 107      * @param defaultChoice The item to display as the pre-selected choice in the dialog.
 108      *        This item must be contained within the choices varargs array.
 109      * @param choices All possible choices to present to the user.
 110      */
 111     public ChoiceDialog(T defaultChoice, Collection<T> choices) {
 112         final DialogPane dialogPane = getDialogPane();
 113 
 114         // -- grid
 115         this.grid = new GridPane();
 116         this.grid.setHgap(10);
 117         this.grid.setMaxWidth(Double.MAX_VALUE);
 118         this.grid.setAlignment(Pos.CENTER_LEFT);
 119 
 120         // -- label
 121         label = DialogPane.createContentLabel(dialogPane.getContentText());
 122         label.setPrefWidth(Region.USE_COMPUTED_SIZE);
 123         label.textProperty().bind(dialogPane.contentTextProperty());
 124 
 125         dialogPane.contentTextProperty().addListener(o -> updateGrid());
 126 
 127         setTitle(ControlResources.getString("Dialog.confirm.title"));
 128         dialogPane.setHeaderText(ControlResources.getString("Dialog.confirm.header"));
 129         dialogPane.getStyleClass().add("choice-dialog");
 130         dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
 131 
 132         final double MIN_WIDTH = 150;
 133 
 134         comboBox = new ComboBox<T>();
 135         comboBox.setMinWidth(MIN_WIDTH);
 136         if (choices != null) {
 137             comboBox.getItems().addAll(choices);
 138         }
 139         comboBox.setMaxWidth(Double.MAX_VALUE);
 140         GridPane.setHgrow(comboBox, Priority.ALWAYS);
 141         GridPane.setFillWidth(comboBox, true);
 142 
 143         this.defaultChoice = comboBox.getItems().contains(defaultChoice) ? defaultChoice : null;
 144 
 145         if (defaultChoice == null) {
 146             comboBox.getSelectionModel().selectFirst();
 147         } else {
 148             comboBox.getSelectionModel().select(defaultChoice);
 149         }
 150 
 151         updateGrid();
 152 
 153         setResultConverter((dialogButton) -> {
 154             ButtonData data = dialogButton == null ? null : dialogButton.getButtonData();
 155             return data == ButtonData.OK_DONE ? getSelectedItem() : null;
 156         });
 157     }
 158 
 159 
 160 
 161     /**************************************************************************
 162      *
 163      * Public API
 164      *
 165      **************************************************************************/
 166 
 167     /**
 168      * Returns the currently selected item in the dialog.
 169      */
 170     public final T getSelectedItem() {
 171         return comboBox.getSelectionModel().getSelectedItem();
 172     }
 173 
 174     /**
 175      * Returns the property representing the currently selected item in the dialog.
 176      */
 177     public final ReadOnlyObjectProperty<T> selectedItemProperty() {
 178         return comboBox.getSelectionModel().selectedItemProperty();
 179     }
 180 
 181     /**
 182      * Sets the currently selected item in the dialog.
 183      * @param item The item to select in the dialog.
 184      */
 185     public final void setSelectedItem(T item) {
 186         comboBox.getSelectionModel().select(item);
 187     }
 188 
 189     /**
 190      * Returns the list of all items that will be displayed to users. This list
 191      * can be modified by the developer to add, remove, or reorder the items
 192      * to present to the user.
 193      */
 194     public final ObservableList<T> getItems() {
 195         return comboBox.getItems();
 196     }
 197 
 198     /**
 199      * Returns the default choice that was specified in the constructor.
 200      */
 201     public final T getDefaultChoice() {
 202         return defaultChoice;
 203     }
 204 
 205 
 206 
 207     /**************************************************************************
 208      *
 209      * Private Implementation
 210      *
 211      **************************************************************************/
 212 
 213     private void updateGrid() {
 214         grid.getChildren().clear();
 215 
 216         grid.add(label, 0, 0);
 217         grid.add(comboBox, 1, 0);
 218         getDialogPane().setContent(grid);
 219 
 220         Platform.runLater(() -> comboBox.requestFocus());
 221     }
 222 }