modules/controls/src/main/java/com/sun/javafx/scene/control/DatePickerContent.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization


   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 com.sun.javafx.scene.control.skin;
  27 
  28 import java.time.DateTimeException;
  29 import java.time.LocalDate;
  30 import java.time.format.DateTimeFormatter;
  31 import java.time.format.DecimalStyle;
  32 import java.time.chrono.Chronology;
  33 import java.time.chrono.ChronoLocalDate;
  34 import java.time.temporal.ChronoUnit;
  35 import java.time.temporal.ValueRange;
  36 import java.time.temporal.WeekFields;
  37 import java.time.YearMonth;
  38 import java.util.ArrayList;
  39 import java.util.List;
  40 import java.util.Locale;
  41 
  42 import static java.time.temporal.ChronoField.*;
  43 import static java.time.temporal.ChronoUnit.*;
  44 

  45 import javafx.application.Platform;
  46 import javafx.beans.property.ObjectProperty;
  47 import javafx.beans.property.SimpleObjectProperty;
  48 import javafx.beans.value.WeakChangeListener;
  49 import javafx.event.EventHandler;
  50 import javafx.scene.Node;
  51 import javafx.scene.Scene;
  52 import javafx.scene.control.Button;
  53 import javafx.scene.control.DatePicker;
  54 import javafx.scene.control.DateCell;
  55 import javafx.scene.control.Label;
  56 import javafx.scene.input.KeyEvent;
  57 import javafx.scene.input.MouseButton;
  58 import javafx.scene.input.MouseEvent;
  59 import javafx.scene.layout.BorderPane;
  60 import javafx.scene.layout.ColumnConstraints;
  61 import javafx.scene.layout.GridPane;
  62 import javafx.scene.layout.HBox;
  63 import javafx.scene.layout.VBox;
  64 import javafx.scene.layout.StackPane;


  98 
  99     final DateTimeFormatter yearFormatter =
 100         DateTimeFormatter.ofPattern("y");
 101 
 102     final DateTimeFormatter yearWithEraFormatter =
 103         DateTimeFormatter.ofPattern("GGGGy"); // For Japanese. What to use for others??
 104 
 105     final DateTimeFormatter weekNumberFormatter =
 106         DateTimeFormatter.ofPattern("w");
 107 
 108     final DateTimeFormatter weekDayNameFormatter =
 109             DateTimeFormatter.ofPattern("ccc"); // Standalone day name
 110 
 111     final DateTimeFormatter dayCellFormatter =
 112         DateTimeFormatter.ofPattern("d");
 113 
 114     static String getString(String key) {
 115         return ControlResources.getString("DatePicker."+key);
 116     }
 117 
 118     DatePickerContent(final DatePicker datePicker) {
 119         this.datePicker = datePicker;
 120 
 121         getStyleClass().add("date-picker-popup");
 122 
 123         daysPerWeek = getDaysPerWeek();
 124 
 125         {
 126             LocalDate date = datePicker.getValue();
 127             displayedYearMonth.set((date != null) ? YearMonth.from(date) : YearMonth.now());
 128         }
 129 
 130         displayedYearMonth.addListener((observable, oldValue, newValue) -> {
 131             updateValues();
 132         });
 133 
 134 
 135         getChildren().add(createMonthYearPane());
 136 
 137         gridPane = new GridPane() {
 138             @Override protected double computePrefWidth(double height) {


 169                     if (oldFocusOwner instanceof DateCell) {
 170                         // Backwards traversal, skip gridPane.
 171                         gridPane.impl_traverse(Direction.PREVIOUS);
 172                     } else {
 173                         // Forwards traversal, pass focus to day cell.
 174                         if (lastFocusedDayCell != null) {
 175                             Platform.runLater(() -> {
 176                                 lastFocusedDayCell.requestFocus();
 177                             });
 178                         } else {
 179                             clearFocus();
 180                         }
 181                     }
 182                 }
 183             });
 184         gridPane.sceneProperty().addListener(new WeakChangeListener<Scene>((ov, oldScene, newScene) -> {
 185             if (oldScene != null) {
 186                 oldScene.focusOwnerProperty().removeListener(weakFocusOwnerListener);
 187             }
 188             if (newScene != null) {

 189                 newScene.focusOwnerProperty().addListener(weakFocusOwnerListener);

 190             }
 191         }));
 192         if (gridPane.getScene() != null) {
 193             gridPane.getScene().focusOwnerProperty().addListener(weakFocusOwnerListener);
 194         }
 195 
 196         // get the weekday labels starting with the weekday that is the
 197         // first-day-of-the-week according to the locale in the
 198         // displayed LocalDate
 199         for (int i = 0; i < daysPerWeek; i++) {
 200             DateCell cell = new DateCell();
 201             cell.getStyleClass().add("day-name-cell");
 202             dayNameCells.add(cell);
 203         }
 204 
 205         // Week number column
 206         for (int i = 0; i < 6; i++) {
 207             DateCell cell = new DateCell();
 208             cell.getStyleClass().add("week-number-cell");
 209             weekNumberCells.add(cell);


 272               case DOWN:
 273               case LEFT:
 274               case RIGHT:
 275               case TAB:
 276                     break;
 277 
 278               case ESCAPE:
 279                 datePicker.hide();
 280                 e.consume();
 281                 break;
 282 
 283               default:
 284                 e.consume();
 285             }
 286         });
 287     }
 288 
 289     private ObjectProperty<YearMonth> displayedYearMonth =
 290         new SimpleObjectProperty<YearMonth>(this, "displayedYearMonth");
 291 
 292     ObjectProperty<YearMonth> displayedYearMonthProperty() {
 293         return displayedYearMonth;
 294     }
 295 
 296 
 297     protected BorderPane createMonthYearPane() {
 298         BorderPane monthYearPane = new BorderPane();
 299         monthYearPane.getStyleClass().add("month-year-pane");
 300 
 301         // Month spinner
 302 
 303         HBox monthSpinner = new HBox();
 304         monthSpinner.getStyleClass().add("spinner");
 305 
 306         backMonthButton = new Button();
 307         backMonthButton.getStyleClass().add("left-button");
 308 
 309         forwardMonthButton = new Button();
 310         forwardMonthButton.getStyleClass().add("right-button");
 311 
 312         StackPane leftMonthArrow = new StackPane();


 351         backYearButton.setGraphic(leftYearArrow);
 352 
 353         StackPane rightYearArrow = new StackPane();
 354         rightYearArrow.getStyleClass().add("right-arrow");
 355         rightYearArrow.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
 356         forwardYearButton.setGraphic(rightYearArrow);
 357 
 358 
 359         backYearButton.setOnAction(t -> {
 360             forward(-1, YEARS, false);
 361         });
 362 
 363         yearLabel = new Label();
 364         yearLabel.getStyleClass().add("spinner-label");
 365 
 366         forwardYearButton.setOnAction(t -> {
 367             forward(1, YEARS, false);
 368         });
 369 
 370         yearSpinner.getChildren().addAll(backYearButton, yearLabel, forwardYearButton);
 371 yearSpinner.setFillHeight(false);
 372         monthYearPane.setRight(yearSpinner);
 373 
 374         return monthYearPane;
 375     }
 376 
 377     private void refresh() {
 378         updateMonthLabelWidth();
 379         updateDayNameCells();
 380         updateValues();
 381     }
 382 
 383     void updateValues() {
 384         // Note: Preserve this order, as DatePickerHijrahContent needs
 385         // updateDayCells before updateMonthYearPane().
 386         updateWeeknumberDateCells();
 387         updateDayCells();
 388         updateMonthYearPane();
 389     }
 390 
 391     void updateGrid() {
 392         gridPane.getColumnConstraints().clear();
 393         gridPane.getChildren().clear();
 394 
 395         int nCols = daysPerWeek + (datePicker.isShowWeekNumbers() ? 1 : 0);
 396 
 397         ColumnConstraints columnConstraints = new ColumnConstraints();
 398         columnConstraints.setPercentWidth(100); // Treated as weight
 399         for (int i = 0; i < nCols; i++) {
 400             gridPane.getColumnConstraints().add(columnConstraints);
 401         }
 402 
 403         for (int i = 0; i < daysPerWeek; i++) {
 404             gridPane.add(dayNameCells.get(i), i + nCols - daysPerWeek, 1);  // col, row
 405         }
 406 
 407         // Week number column
 408         if (datePicker.isShowWeekNumbers()) {
 409             for (int i = 0; i < 6; i++) {
 410                 gridPane.add(weekNumberCells.get(i), 0, i + 2);  // col, row
 411             }
 412         }
 413 
 414         // setup: 6 rows of daysPerWeek (which is the maximum number of cells required in the worst case layout)
 415         for (int row = 0; row < 6; row++) {
 416             for (int col = 0; col < daysPerWeek; col++) {
 417                 gridPane.add(dayCells.get(row*daysPerWeek+col), col + nCols - daysPerWeek, row + 2);
 418             }
 419         }
 420     }
 421 
 422     void updateDayNameCells() {
 423         // first day of week, 1 = monday, 7 = sunday
 424         int firstDayOfWeek = WeekFields.of(getLocale()).getFirstDayOfWeek().getValue();
 425 
 426         // july 13th 2009 is a Monday, so a firstDayOfWeek=1 must come out of the 13th
 427         LocalDate date = LocalDate.of(2009, 7, 12 + firstDayOfWeek);
 428         for (int i = 0; i < daysPerWeek; i++) {
 429             String name = weekDayNameFormatter.withLocale(getLocale()).format(date.plus(i, DAYS));
 430             dayNameCells.get(i).setText(titleCaseWord(name));
 431         }
 432     }
 433 
 434     void updateWeeknumberDateCells() {
 435         if (datePicker.isShowWeekNumbers()) {
 436             final Locale locale = getLocale();
 437             final int maxWeeksPerMonth = 6; // TODO: Get this from chronology?
 438 
 439             LocalDate firstOfMonth = displayedYearMonth.get().atDay(1);
 440             for (int i = 0; i < maxWeeksPerMonth; i++) {
 441                 LocalDate date = firstOfMonth.plus(i, WEEKS);
 442                 // Use a formatter to ensure correct localization,
 443                 // such as when Thai numerals are required.
 444                 String cellText =
 445                     weekNumberFormatter.withLocale(locale)
 446                                        .withDecimalStyle(DecimalStyle.of(locale))
 447                                        .format(date);
 448                 weekNumberCells.get(i).setText(cellText);
 449             }
 450         }
 451     }
 452 
 453     void updateDayCells() {
 454         Locale locale = getLocale();
 455         Chronology chrono = getPrimaryChronology();
 456         int firstOfMonthIdx = determineFirstOfMonthDayOfWeek();
 457         YearMonth curMonth = displayedYearMonth.get();
 458 
 459         // RT-31075: The following are now set in the try-catch block.
 460         YearMonth prevMonth = null;
 461         YearMonth nextMonth = null;
 462         int daysInCurMonth = -1;
 463         int daysInPrevMonth = -1;
 464         int daysInNextMonth = -1;
 465 
 466         for (int i = 0; i < 6 * daysPerWeek; i++) {
 467             DateCell dayCell = dayCells.get(i);
 468             dayCell.getStyleClass().setAll("cell", "date-cell", "day-cell");
 469             dayCell.setDisable(false);
 470             dayCell.setStyle(null);
 471             dayCell.setGraphic(null);
 472             dayCell.setTooltip(null);
 473 


 682                 findDayCellForDate(date).requestFocus();
 683             }
 684         }
 685     }
 686 
 687     // public for behavior class
 688     public void selectDayCell(DateCell dateCell) {
 689         datePicker.setValue(dayCellDate(dateCell));
 690         datePicker.hide();
 691     }
 692 
 693     private DateCell findDayCellForDate(LocalDate date) {
 694         for (int i = 0; i < dayCellDates.length; i++) {
 695             if (date.equals(dayCellDates[i])) {
 696                 return dayCells.get(i);
 697             }
 698         }
 699         return dayCells.get(dayCells.size()/2+1);
 700     }
 701 
 702     void clearFocus() {
 703         LocalDate focusDate = datePicker.getValue();
 704         if (focusDate == null) {
 705             focusDate = LocalDate.now();
 706         }
 707         if (YearMonth.from(focusDate).equals(displayedYearMonth.get())) {
 708             // focus date
 709             goToDate(focusDate, true);
 710         } else {
 711             // focus month spinner (should not happen)
 712             backMonthButton.requestFocus();
 713         }
 714 
 715         // RT-31857
 716         if (backMonthButton.getWidth() == 0) {
 717             backMonthButton.requestLayout();
 718             forwardMonthButton.requestLayout();
 719             backYearButton.requestLayout();
 720             forwardYearButton.requestLayout();
 721         }
 722     }




   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 com.sun.javafx.scene.control;
  27 
  28 import java.time.DateTimeException;
  29 import java.time.LocalDate;
  30 import java.time.format.DateTimeFormatter;
  31 import java.time.format.DecimalStyle;
  32 import java.time.chrono.Chronology;
  33 import java.time.chrono.ChronoLocalDate;
  34 import java.time.temporal.ChronoUnit;
  35 import java.time.temporal.ValueRange;
  36 import java.time.temporal.WeekFields;
  37 import java.time.YearMonth;
  38 import java.util.ArrayList;
  39 import java.util.List;
  40 import java.util.Locale;
  41 
  42 import static java.time.temporal.ChronoField.*;
  43 import static java.time.temporal.ChronoUnit.*;
  44 
  45 import com.sun.javafx.scene.control.skin.*;
  46 import javafx.application.Platform;
  47 import javafx.beans.property.ObjectProperty;
  48 import javafx.beans.property.SimpleObjectProperty;
  49 import javafx.beans.value.WeakChangeListener;
  50 import javafx.event.EventHandler;
  51 import javafx.scene.Node;
  52 import javafx.scene.Scene;
  53 import javafx.scene.control.Button;
  54 import javafx.scene.control.DatePicker;
  55 import javafx.scene.control.DateCell;
  56 import javafx.scene.control.Label;
  57 import javafx.scene.input.KeyEvent;
  58 import javafx.scene.input.MouseButton;
  59 import javafx.scene.input.MouseEvent;
  60 import javafx.scene.layout.BorderPane;
  61 import javafx.scene.layout.ColumnConstraints;
  62 import javafx.scene.layout.GridPane;
  63 import javafx.scene.layout.HBox;
  64 import javafx.scene.layout.VBox;
  65 import javafx.scene.layout.StackPane;


  99 
 100     final DateTimeFormatter yearFormatter =
 101         DateTimeFormatter.ofPattern("y");
 102 
 103     final DateTimeFormatter yearWithEraFormatter =
 104         DateTimeFormatter.ofPattern("GGGGy"); // For Japanese. What to use for others??
 105 
 106     final DateTimeFormatter weekNumberFormatter =
 107         DateTimeFormatter.ofPattern("w");
 108 
 109     final DateTimeFormatter weekDayNameFormatter =
 110             DateTimeFormatter.ofPattern("ccc"); // Standalone day name
 111 
 112     final DateTimeFormatter dayCellFormatter =
 113         DateTimeFormatter.ofPattern("d");
 114 
 115     static String getString(String key) {
 116         return ControlResources.getString("DatePicker."+key);
 117     }
 118 
 119     public DatePickerContent(final DatePicker datePicker) {
 120         this.datePicker = datePicker;
 121 
 122         getStyleClass().add("date-picker-popup");
 123 
 124         daysPerWeek = getDaysPerWeek();
 125 
 126         {
 127             LocalDate date = datePicker.getValue();
 128             displayedYearMonth.set((date != null) ? YearMonth.from(date) : YearMonth.now());
 129         }
 130 
 131         displayedYearMonth.addListener((observable, oldValue, newValue) -> {
 132             updateValues();
 133         });
 134 
 135 
 136         getChildren().add(createMonthYearPane());
 137 
 138         gridPane = new GridPane() {
 139             @Override protected double computePrefWidth(double height) {


 170                     if (oldFocusOwner instanceof DateCell) {
 171                         // Backwards traversal, skip gridPane.
 172                         gridPane.impl_traverse(Direction.PREVIOUS);
 173                     } else {
 174                         // Forwards traversal, pass focus to day cell.
 175                         if (lastFocusedDayCell != null) {
 176                             Platform.runLater(() -> {
 177                                 lastFocusedDayCell.requestFocus();
 178                             });
 179                         } else {
 180                             clearFocus();
 181                         }
 182                     }
 183                 }
 184             });
 185         gridPane.sceneProperty().addListener(new WeakChangeListener<Scene>((ov, oldScene, newScene) -> {
 186             if (oldScene != null) {
 187                 oldScene.focusOwnerProperty().removeListener(weakFocusOwnerListener);
 188             }
 189             if (newScene != null) {
 190                 Platform.runLater(() -> {
 191                     newScene.focusOwnerProperty().addListener(weakFocusOwnerListener);
 192                 });
 193             }
 194         }));
 195         if (gridPane.getScene() != null) {
 196             gridPane.getScene().focusOwnerProperty().addListener(weakFocusOwnerListener);
 197         }
 198 
 199         // get the weekday labels starting with the weekday that is the
 200         // first-day-of-the-week according to the locale in the
 201         // displayed LocalDate
 202         for (int i = 0; i < daysPerWeek; i++) {
 203             DateCell cell = new DateCell();
 204             cell.getStyleClass().add("day-name-cell");
 205             dayNameCells.add(cell);
 206         }
 207 
 208         // Week number column
 209         for (int i = 0; i < 6; i++) {
 210             DateCell cell = new DateCell();
 211             cell.getStyleClass().add("week-number-cell");
 212             weekNumberCells.add(cell);


 275               case DOWN:
 276               case LEFT:
 277               case RIGHT:
 278               case TAB:
 279                     break;
 280 
 281               case ESCAPE:
 282                 datePicker.hide();
 283                 e.consume();
 284                 break;
 285 
 286               default:
 287                 e.consume();
 288             }
 289         });
 290     }
 291 
 292     private ObjectProperty<YearMonth> displayedYearMonth =
 293         new SimpleObjectProperty<YearMonth>(this, "displayedYearMonth");
 294 
 295     public ObjectProperty<YearMonth> displayedYearMonthProperty() {
 296         return displayedYearMonth;
 297     }
 298 
 299 
 300     protected BorderPane createMonthYearPane() {
 301         BorderPane monthYearPane = new BorderPane();
 302         monthYearPane.getStyleClass().add("month-year-pane");
 303 
 304         // Month spinner
 305 
 306         HBox monthSpinner = new HBox();
 307         monthSpinner.getStyleClass().add("spinner");
 308 
 309         backMonthButton = new Button();
 310         backMonthButton.getStyleClass().add("left-button");
 311 
 312         forwardMonthButton = new Button();
 313         forwardMonthButton.getStyleClass().add("right-button");
 314 
 315         StackPane leftMonthArrow = new StackPane();


 354         backYearButton.setGraphic(leftYearArrow);
 355 
 356         StackPane rightYearArrow = new StackPane();
 357         rightYearArrow.getStyleClass().add("right-arrow");
 358         rightYearArrow.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
 359         forwardYearButton.setGraphic(rightYearArrow);
 360 
 361 
 362         backYearButton.setOnAction(t -> {
 363             forward(-1, YEARS, false);
 364         });
 365 
 366         yearLabel = new Label();
 367         yearLabel.getStyleClass().add("spinner-label");
 368 
 369         forwardYearButton.setOnAction(t -> {
 370             forward(1, YEARS, false);
 371         });
 372 
 373         yearSpinner.getChildren().addAll(backYearButton, yearLabel, forwardYearButton);
 374         yearSpinner.setFillHeight(false);
 375         monthYearPane.setRight(yearSpinner);
 376 
 377         return monthYearPane;
 378     }
 379 
 380     private void refresh() {
 381         updateMonthLabelWidth();
 382         updateDayNameCells();
 383         updateValues();
 384     }
 385 
 386     public void updateValues() {
 387         // Note: Preserve this order, as DatePickerHijrahContent needs
 388         // updateDayCells before updateMonthYearPane().
 389         updateWeeknumberDateCells();
 390         updateDayCells();
 391         updateMonthYearPane();
 392     }
 393 
 394     public void updateGrid() {
 395         gridPane.getColumnConstraints().clear();
 396         gridPane.getChildren().clear();
 397 
 398         int nCols = daysPerWeek + (datePicker.isShowWeekNumbers() ? 1 : 0);
 399 
 400         ColumnConstraints columnConstraints = new ColumnConstraints();
 401         columnConstraints.setPercentWidth(100); // Treated as weight
 402         for (int i = 0; i < nCols; i++) {
 403             gridPane.getColumnConstraints().add(columnConstraints);
 404         }
 405 
 406         for (int i = 0; i < daysPerWeek; i++) {
 407             gridPane.add(dayNameCells.get(i), i + nCols - daysPerWeek, 1);  // col, row
 408         }
 409 
 410         // Week number column
 411         if (datePicker.isShowWeekNumbers()) {
 412             for (int i = 0; i < 6; i++) {
 413                 gridPane.add(weekNumberCells.get(i), 0, i + 2);  // col, row
 414             }
 415         }
 416 
 417         // setup: 6 rows of daysPerWeek (which is the maximum number of cells required in the worst case layout)
 418         for (int row = 0; row < 6; row++) {
 419             for (int col = 0; col < daysPerWeek; col++) {
 420                 gridPane.add(dayCells.get(row*daysPerWeek+col), col + nCols - daysPerWeek, row + 2);
 421             }
 422         }
 423     }
 424 
 425     public void updateDayNameCells() {
 426         // first day of week, 1 = monday, 7 = sunday
 427         int firstDayOfWeek = WeekFields.of(getLocale()).getFirstDayOfWeek().getValue();
 428 
 429         // july 13th 2009 is a Monday, so a firstDayOfWeek=1 must come out of the 13th
 430         LocalDate date = LocalDate.of(2009, 7, 12 + firstDayOfWeek);
 431         for (int i = 0; i < daysPerWeek; i++) {
 432             String name = weekDayNameFormatter.withLocale(getLocale()).format(date.plus(i, DAYS));
 433             dayNameCells.get(i).setText(titleCaseWord(name));
 434         }
 435     }
 436 
 437     public void updateWeeknumberDateCells() {
 438         if (datePicker.isShowWeekNumbers()) {
 439             final Locale locale = getLocale();
 440             final int maxWeeksPerMonth = 6; // TODO: Get this from chronology?
 441 
 442             LocalDate firstOfMonth = displayedYearMonth.get().atDay(1);
 443             for (int i = 0; i < maxWeeksPerMonth; i++) {
 444                 LocalDate date = firstOfMonth.plus(i, WEEKS);
 445                 // Use a formatter to ensure correct localization,
 446                 // such as when Thai numerals are required.
 447                 String cellText =
 448                     weekNumberFormatter.withLocale(locale)
 449                                        .withDecimalStyle(DecimalStyle.of(locale))
 450                                        .format(date);
 451                 weekNumberCells.get(i).setText(cellText);
 452             }
 453         }
 454     }
 455 
 456     public void updateDayCells() {
 457         Locale locale = getLocale();
 458         Chronology chrono = getPrimaryChronology();
 459         int firstOfMonthIdx = determineFirstOfMonthDayOfWeek();
 460         YearMonth curMonth = displayedYearMonth.get();
 461 
 462         // RT-31075: The following are now set in the try-catch block.
 463         YearMonth prevMonth = null;
 464         YearMonth nextMonth = null;
 465         int daysInCurMonth = -1;
 466         int daysInPrevMonth = -1;
 467         int daysInNextMonth = -1;
 468 
 469         for (int i = 0; i < 6 * daysPerWeek; i++) {
 470             DateCell dayCell = dayCells.get(i);
 471             dayCell.getStyleClass().setAll("cell", "date-cell", "day-cell");
 472             dayCell.setDisable(false);
 473             dayCell.setStyle(null);
 474             dayCell.setGraphic(null);
 475             dayCell.setTooltip(null);
 476 


 685                 findDayCellForDate(date).requestFocus();
 686             }
 687         }
 688     }
 689 
 690     // public for behavior class
 691     public void selectDayCell(DateCell dateCell) {
 692         datePicker.setValue(dayCellDate(dateCell));
 693         datePicker.hide();
 694     }
 695 
 696     private DateCell findDayCellForDate(LocalDate date) {
 697         for (int i = 0; i < dayCellDates.length; i++) {
 698             if (date.equals(dayCellDates[i])) {
 699                 return dayCells.get(i);
 700             }
 701         }
 702         return dayCells.get(dayCells.size()/2+1);
 703     }
 704 
 705     public void clearFocus() {
 706         LocalDate focusDate = datePicker.getValue();
 707         if (focusDate == null) {
 708             focusDate = LocalDate.now();
 709         }
 710         if (YearMonth.from(focusDate).equals(displayedYearMonth.get())) {
 711             // focus date
 712             goToDate(focusDate, true);
 713         } else {
 714             // focus month spinner (should not happen)
 715             backMonthButton.requestFocus();
 716         }
 717 
 718         // RT-31857
 719         if (backMonthButton.getWidth() == 0) {
 720             backMonthButton.requestLayout();
 721             forwardMonthButton.requestLayout();
 722             backYearButton.requestLayout();
 723             forwardYearButton.requestLayout();
 724         }
 725     }