/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (DecadeView.java) is part of project Time4J. * * Time4J is free software: You can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * Time4J is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Time4J. If not, see <http://www.gnu.org/licenses/>. * ----------------------------------------------------------------------- */ package net.time4j.ui.javafx; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import net.time4j.PlainDate; import net.time4j.engine.CalendarDate; import net.time4j.engine.CalendarVariant; import net.time4j.engine.EpochDays; import net.time4j.format.CalendarText; import net.time4j.format.DisplayMode; import net.time4j.format.expert.ChronoFormatter; import net.time4j.format.expert.PatternType; import net.time4j.range.CalendarYear; import net.time4j.range.DateInterval; import java.util.Locale; class DecadeView<T extends CalendarDate> extends TableView<T> { //~ Statische Felder/Initialisierungen -------------------------------- private static final String CSS_CALENDAR_DECADE_VIEW = "calendar-decade-view"; private static final String CSS_CALENDAR_CELL_INSIDE_RANGE = "calendar-cell-inside-range"; private static final String CSS_CALENDAR_CELL_OUT_OF_RANGE = "calendar-cell-out-of-range"; //~ Konstruktoren ----------------------------------------------------- protected DecadeView( CalendarControl<T> control, FXCalendarSystem<T> calsys, boolean animationMode ) { super(control, calsys, animationMode); getStyleClass().add(CSS_CALENDAR_DECADE_VIEW); if (!this.isAnimationMode()) { // listen to arrow keys this.setEventHandler( KeyEvent.KEY_PRESSED, event -> { KeyCode code = event.getCode(); if (code.isArrowKey()) { int index = -1; for (int i = 0; i < 12; i++) { Node cell = getChildren().get(i); if (cell.isFocused()) { index = i; break; } } if (index != -1) { int rowIndex = index / 4; int colIndex = index % 4; switch (code) { case UP: rowIndex = Math.max(0, rowIndex - 1); break; case RIGHT: colIndex = Math.min(3, colIndex + 1); break; case DOWN: rowIndex = Math.min(2, rowIndex + 1); break; case LEFT: colIndex = Math.max(0, colIndex - 1); break; default: return; } int newIndex = rowIndex * 4 + colIndex; Node cell = getChildren().get(newIndex); if (!cell.isDisabled()) { cell.requestFocus(); } } event.consume(); } } ); } } //~ Methoden ---------------------------------------------------------- @Override protected void buildContent() { for (int i = 0; i < 12; i++) { Button button = new Button(); if (i == 0 || i == 11) { button.getStyleClass().add(CSS_CALENDAR_CELL_OUT_OF_RANGE); } else { button.getStyleClass().add(CSS_CALENDAR_CELL_INSIDE_RANGE); } button.setMaxWidth(Double.MAX_VALUE); button.setMaxHeight(Double.MAX_VALUE); GridPane.setVgrow(button, Priority.ALWAYS); GridPane.setHgrow(button, Priority.ALWAYS); button.setOnAction( actionEvent -> { if (getControl().viewIndexProperty().get() == NavigationBar.BIRD_VIEW) { T clickedDate = getControl().chronology().getChronoType().cast(button.getUserData()); getControl().pageDateProperty().setValue(clickedDate); getControl().viewIndexProperty().set(NavigationBar.YEAR_VIEW); } } ); int colIndex = i % 4; int rowIndex = i / 4; add(button, colIndex, rowIndex); } } @Override protected void updateContent(T date) { Locale locale = this.getControl().localeProperty().get(); if (locale == null) { locale = Locale.ROOT; } FXCalendarSystem<T> cs = this.getCalendarSystem(); date = cs.withFirstDayOfYear(date); // should be successful int yearsAfterStartOfDecade = Math.floorMod(cs.getProlepticYear(date), 10); T min = this.getControl().minDateProperty().get(); T max = this.getControl().maxDateProperty().get(); String pattern = CalendarYear.chronology().getFormatPattern(DisplayMode.FULL, locale); if (!(date instanceof PlainDate)) { if (this.getControl().chronology().getFormatPattern(DisplayMode.MEDIUM, locale).endsWith("G")) { pattern = pattern + " G"; } else { pattern = "G " + pattern; } } ChronoFormatter<T> yearFormat = ChronoFormatter.ofPattern( pattern, PatternType.CLDR, locale, this.getControl().chronology() ); T startYear = null; T endYear = null; for (int i = 0; i < 12; i++) { Button button = (Button) getChildren().get(i); try { T btnDate = cs.addYears(date, i - 1 - yearsAfterStartOfDecade); if ( cs.withLastDayOfYear(btnDate).isBefore(min) || cs.withFirstDayOfYear(btnDate).isAfter(max) ) { button.setDisable(true); button.setText(" "); button.setUserData(null); } else { if ((startYear == null) && (i >= 1)) { startYear = btnDate; } if (i <= 10) { endYear = btnDate; } button.setDisable(false); button.setText(yearFormat.format(btnDate)); button.setUserData(btnDate); } } catch (ArithmeticException | IllegalArgumentException ex) { button.setDisable(true); button.setText(" "); button.setUserData(null); } } if (!this.isAnimationMode()) { this.titleProperty().setValue(this.getNavigationTitle(startYear, endYear, yearFormat, locale)); } } @Override protected int getViewIndex() { return NavigationBar.BIRD_VIEW; } private String getNavigationTitle( T startYear, T endYear, ChronoFormatter<T> printer, Locale locale ) { T start; T end; try { start = this.getCalendarSystem().withFirstDayOfYear(startYear); } catch (IllegalArgumentException ex) { start = this.getCalendarSystem().getChronologicalMinimum(); } try { end = this.getCalendarSystem().withLastDayOfYear(endYear); } catch (IllegalArgumentException ex) { end = this.getCalendarSystem().getChronologicalMaximum(); } String intervalPattern = CalendarText.patternForInterval(locale); StringBuilder sb = new StringBuilder(16); int i = 0; int n = intervalPattern.length(); while (i < n) { char c = intervalPattern.charAt(i); if ((c == '{') && (i + 2 < n) && (intervalPattern.charAt(i + 2) == '}')) { char next = intervalPattern.charAt(i + 1); if (next == '0') { printer.print(start, sb); i += 3; continue; } else if (next == '1') { printer.print(end, sb); i += 3; continue; } } sb.append(c); i++; } DateInterval range = DateInterval.between( PlainDate.of(start.getDaysSinceEpochUTC(), EpochDays.UTC), PlainDate.of(end.getDaysSinceEpochUTC(), EpochDays.UTC) ); String prefix; if (start instanceof CalendarVariant) { prefix = CalendarVariant.class.cast(start).getVariant(); } else { prefix = getCalendarSystem().getCalendarType(); } this.infoProperty().setValue(prefix + ": " + range.toString()); return sb.toString(); } }