1 /* 2 * Copyright (c) 2013, 2015, 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 26 package javafx.scene.control.skin; 27 28 import java.time.LocalDate; 29 import java.time.YearMonth; 30 import java.time.chrono.HijrahChronology; 31 32 import com.sun.javafx.scene.control.DatePickerContent; 33 import com.sun.javafx.scene.control.DatePickerHijrahContent; 34 import com.sun.javafx.scene.control.behavior.BehaviorBase; 35 import com.sun.javafx.scene.control.behavior.ComboBoxBaseBehavior; 36 import javafx.beans.InvalidationListener; 37 import javafx.beans.Observable; 38 import javafx.event.ActionEvent; 39 import javafx.geometry.Insets; 40 import javafx.scene.Node; 41 import javafx.scene.control.Button; 42 import javafx.scene.control.Control; 43 import javafx.scene.control.DatePicker; 44 import javafx.scene.control.TextField; 45 import javafx.util.StringConverter; 46 47 import com.sun.javafx.scene.control.behavior.DatePickerBehavior; 48 49 /** 50 * Default skin implementation for the {@link DatePicker} control. 51 * 52 * @see DatePicker 53 * @since 9 54 */ 55 public class DatePickerSkin extends ComboBoxPopupControl<LocalDate> { 56 57 /*************************************************************************** 58 * * 59 * Private fields * 60 * * 61 **************************************************************************/ 62 63 private final DatePicker datePicker; 64 private TextField displayNode; 65 private DatePickerContent datePickerContent; 66 67 private final DatePickerBehavior behavior; 68 69 70 71 /*************************************************************************** 72 * * 73 * Constructors * 74 * * 75 **************************************************************************/ 76 77 /** 78 * Creates a new DatePickerSkin instance, installing the necessary child 79 * nodes into the Control {@link Control#getChildren() children} list, as 80 * well as the necessary input mappings for handling key, mouse, etc events. 81 * 82 * @param control The control that this skin should be installed onto. 83 */ 84 public DatePickerSkin(final DatePicker control) { 85 super(control); 86 87 this.datePicker = control; 88 89 // install default input map for the control 90 this.behavior = new DatePickerBehavior(control); 91 // control.setInputMap(behavior.getInputMap()); 92 93 // The "arrow" is actually a rectangular svg icon resembling a calendar. 94 // Round the size of the icon to whole integers to get sharp edges. 95 arrow.paddingProperty().addListener(new InvalidationListener() { 96 // This boolean protects against unwanted recursion. 97 private boolean rounding = false; 98 @Override public void invalidated(Observable observable) { 99 if (!rounding) { 100 Insets padding = arrow.getPadding(); 101 Insets rounded = new Insets(Math.round(padding.getTop()), Math.round(padding.getRight()), 102 Math.round(padding.getBottom()), Math.round(padding.getLeft())); 103 if (!rounded.equals(padding)) { 104 rounding = true; 105 arrow.setPadding(rounded); 106 rounding = false; 107 } 108 } 109 } 110 }); 111 112 registerChangeListener(control.chronologyProperty(), e -> { 113 updateDisplayNode(); 114 datePickerContent = null; 115 popup = null; 116 }); 117 registerChangeListener(control.converterProperty(), e -> updateDisplayNode()); 118 registerChangeListener(control.dayCellFactoryProperty(), e -> { 119 updateDisplayNode(); 120 datePickerContent = null; 121 popup = null; 122 }); 123 registerChangeListener(control.showWeekNumbersProperty(), e -> { 124 if (datePickerContent != null) { 125 datePickerContent.updateGrid(); 126 datePickerContent.updateWeeknumberDateCells(); 127 } 128 }); 129 registerChangeListener(control.valueProperty(), e -> { 130 updateDisplayNode(); 131 if (datePickerContent != null) { 132 LocalDate date = control.getValue(); 133 datePickerContent.displayedYearMonthProperty().set((date != null) ? YearMonth.from(date) : YearMonth.now()); 134 datePickerContent.updateValues(); 135 } 136 control.fireEvent(new ActionEvent()); 137 }); 138 registerChangeListener(control.showingProperty(), e -> { 139 if (control.isShowing()) { 140 if (datePickerContent != null) { 141 LocalDate date = control.getValue(); 142 datePickerContent.displayedYearMonthProperty().set((date != null) ? YearMonth.from(date) : YearMonth.now()); 143 datePickerContent.updateValues(); 144 } 145 show(); 146 } else { 147 hide(); 148 } 149 }); 150 } 151 152 153 154 /*************************************************************************** 155 * * 156 * Public API * 157 * * 158 **************************************************************************/ 159 160 /** {@inheritDoc} */ 161 @Override public void dispose() { 162 super.dispose(); 163 164 if (behavior != null) { 165 behavior.dispose(); 166 } 167 } 168 169 /** {@inheritDoc} */ 170 @Override public Node getPopupContent() { 171 if (datePickerContent == null) { 172 if (datePicker.getChronology() instanceof HijrahChronology) { 173 datePickerContent = new DatePickerHijrahContent(datePicker); 174 } else { 175 datePickerContent = new DatePickerContent(datePicker); 176 } 177 } 178 179 return datePickerContent; 180 } 181 182 /** {@inheritDoc} */ 183 @Override protected double computeMinWidth(double height, 184 double topInset, double rightInset, 185 double bottomInset, double leftInset) { 186 return 50; 187 } 188 189 /** {@inheritDoc} */ 190 @Override public void show() { 191 super.show(); 192 datePickerContent.clearFocus(); 193 } 194 195 /** {@inheritDoc} */ 196 @Override protected TextField getEditor() { 197 // Use getSkinnable() here because this method is called from 198 // the super constructor before datePicker is initialized. 199 return ((DatePicker)getSkinnable()).getEditor(); 200 } 201 202 /** {@inheritDoc} */ 203 @Override protected StringConverter<LocalDate> getConverter() { 204 return ((DatePicker)getSkinnable()).getConverter(); 205 } 206 207 /** {@inheritDoc} */ 208 @Override public Node getDisplayNode() { 209 if (displayNode == null) { 210 displayNode = getEditableInputNode(); 211 displayNode.getStyleClass().add("date-picker-display-node"); 212 updateDisplayNode(); 213 } 214 displayNode.setEditable(datePicker.isEditable()); 215 216 return displayNode; 217 } 218 219 220 221 /*************************************************************************** 222 * * 223 * Private implementation * 224 * * 225 **************************************************************************/ 226 227 /** {@inheritDoc} */ 228 @Override void focusLost() { 229 // do nothing 230 } 231 232 /** {@inheritDoc} */ 233 @Override ComboBoxBaseBehavior getBehavior() { 234 return behavior; 235 } 236 }