/*
* Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.fxcontrols.weekview;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.VPos;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public class WeekView<T> extends GridPane {
private static final Logger log = LoggerFactory.getLogger(WeekView.class);
public static final int HEIGHT_OF_HOUR = 60;
public static final int WIDTH_OF_TIMECOLUMN = 80;
public static final double PERCENT_WIDTH_DAY_COLUMN = 13;
public static final double PERCENT_WIDTH_TIME_COLUMN = 7;
public static final double INSETS_WHOLEDAY = 2.5;
protected final ObservableList<WeekViewAppointment<T>> entries = FXCollections.observableArrayList();
protected final SimpleIntegerProperty weekOfYear = new SimpleIntegerProperty();
protected final SimpleIntegerProperty year = new SimpleIntegerProperty();
protected final ObjectProperty<BiConsumer<LocalDate, LocalTime>> onAppointmentCreation = new SimpleObjectProperty<>();
protected final ObjectProperty<AppointmentResolver<T>> appointmentResolver = new SimpleObjectProperty<>();
protected final GridPane contentPane = new GridPane();
protected final ScrollPane contentScollPane = new ScrollPane();
protected final GridPane wholeDayPane = new GridPane();
protected final WeekTitle title;
protected final List<Label> weekDayLabels = new LinkedList<>();
protected final WeekHelper helper = new WeekHelper();
protected final Table<Integer, Integer, StackPane> cells = HashBasedTable.create();
protected final Map<Integer, VBox> wholeDayCells = new TreeMap<>();
protected boolean recomupting = false;
protected int lastRow = -1;
protected int currentEntryStyleNr = 1;
protected final SimpleDoubleProperty hourHeight = new SimpleDoubleProperty(HEIGHT_OF_HOUR);
public WeekView(String today) {
title = new WeekTitle(today, weekOfYear, year);
sceneProperty().addListener((p, o, n) -> {
String styleSheetPath = WeekView.class.getResource("weekview.css").toExternalForm();
if (n != null) {
ObservableList<String> stylesheets = n.getStylesheets();
if (!stylesheets.contains(styleSheetPath)) {
stylesheets.add(styleSheetPath);
}
} else {
o.getStylesheets().remove(styleSheetPath);
}
});
configureRootPane();
configureContentPane();
configureWholeDayPane();
contentScollPane.setVvalue(0.5);
configureScollPane(contentScollPane, HEIGHT_OF_HOUR);
weekOfYear.addListener((p, o, n) -> {
recompute();
});
year.addListener((p, o, n) -> {
recompute();
});
LocalDate now = LocalDate.now();
weekOfYear.set(helper.getWeek(now));
year.set(now.getYear());
Platform.runLater(() -> contentScollPane.setVvalue(0.5));
appointmentResolver.addListener((p, o, n) -> {
if (o == null && n != null) {
recreateEntries();
}
});
entries.addListener(this::entriesChanged);
contentPane.setOnScroll(m -> {
if (m.isControlDown()) {
double vvalue = contentScollPane.getVvalue();
double deltaY = m.getDeltaY();
double lastHeight = hourHeight.getValue();
double newHeight = Math.max(HEIGHT_OF_HOUR, lastHeight + deltaY);
this.hourHeight.set(newHeight);
double v = newHeight / lastHeight;
contentScollPane.setVvalue(vvalue * v);
m.consume();
recreateEntries(new ArrayList<>(entries));
}
});
}
protected void configureScollPane(ScrollPane scrollPane, double minHeight) {
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setMinSize(Control.USE_COMPUTED_SIZE, minHeight);
scrollPane.setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
}
protected void configureRootPane() {
setMinSize(350, HEIGHT_OF_HOUR);
setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
setMaxSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
getRowConstraints().add(new RowConstraints(Control.USE_PREF_SIZE, Control.USE_COMPUTED_SIZE, Control.USE_PREF_SIZE, Priority.NEVER, VPos.BOTTOM, true));
getRowConstraints().add(new RowConstraints(Control.USE_PREF_SIZE, 30, Control.USE_COMPUTED_SIZE, Priority.NEVER, VPos.BOTTOM, true));
getRowConstraints().add(new RowConstraints(100, Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE, Priority.ALWAYS, VPos.TOP, true));
ColumnConstraints timeColumn = new ColumnConstraints(WIDTH_OF_TIMECOLUMN, WIDTH_OF_TIMECOLUMN, Control.USE_PREF_SIZE, Priority.NEVER, HPos.RIGHT, true);
timeColumn.setPercentWidth(PERCENT_WIDTH_TIME_COLUMN);
getColumnConstraints().add(timeColumn);
for (int i = 1; i <= 7; i++) {
ColumnConstraints constraints = new ColumnConstraints(10, 80, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true);
constraints.setPercentWidth(PERCENT_WIDTH_DAY_COLUMN);
getColumnConstraints().add(constraints);
Label label = new Label();
label.getStyleClass().add("week-daytitle");
add(label, i, 1);
GridPane.setMargin(label, new Insets(0, 0, 5, 0));
weekDayLabels.add(label);
}
ColumnConstraints lastColumn = new ColumnConstraints(10, 10, 10, Priority.NEVER, HPos.RIGHT, true);
lastColumn.setPercentWidth(2);
getColumnConstraints().add(lastColumn);
add(title, 0, 0, GridPane.REMAINING, 1);
SplitPane splitPane = new SplitPane();
splitPane.setDividerPosition(0, 0.8);
splitPane.setOrientation(Orientation.VERTICAL);
splitPane.getItems().add(contentScollPane);
splitPane.getItems().add(wholeDayPane);
add(splitPane, 0, 2, GridPane.REMAINING, 1);
contentScollPane.setContent(contentPane);
}
protected void configureContentPane() {
createTimeAndDayColumns(contentPane);
for (int i = 0; i < 24; i++) {
RowConstraints rowConstraints = new RowConstraints(10, HEIGHT_OF_HOUR, Control.USE_PREF_SIZE, Priority.NEVER, VPos.TOP, true);
this.hourHeight.addListener((p, o, n) -> {
rowConstraints.setPrefHeight(n.doubleValue());
});
contentPane.getRowConstraints().add(rowConstraints);
StackPane background = new StackPane();
String styleClass = i % 2 == 0 ? "week-bg-even" : "week-bg-odd";
background.getStyleClass().add(styleClass);
contentPane.add(background, 0, i, Integer.MAX_VALUE, 1);
for (int j = 0; j < 8; j++) {
StackPane cell = createHourCell(i, j);
cells.put(i, j, cell);
contentPane.add(cell, j, i);
}
Label time = new Label();
time.setText(String.format("%02d", i) + ":00");
contentPane.add(time, 0, i);
Separator separator = new Separator();
separator.setOrientation(Orientation.HORIZONTAL);
separator.setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
contentPane.add(separator, 0, i, Integer.MAX_VALUE, 1);
}
createSeparators(contentPane);
contentPane.setOnMouseMoved(e -> {
double x = e.getX();
double y = e.getY();
int row = (int) (y / HEIGHT_OF_HOUR);
double factor = (contentPane.getWidth() - WIDTH_OF_TIMECOLUMN) / 7;
int column = (int) ((x - WIDTH_OF_TIMECOLUMN) / factor) + 1;
if (row != lastRow) {
String currentCellStyle = row % 2 == 0 ? "week-bg-even-hover" : "week-bg-odd-hover";
String lastCellStyle = lastRow % 2 == 0 ? "week-bg-even-hover" : "week-bg-odd-hover";
cells.row(lastRow).values().forEach(cell -> {
cell.getStyleClass().remove(lastCellStyle);
});
cells.row(row).values().forEach(cell -> cell.getStyleClass().add(currentCellStyle));
lastRow = row;
}
});
}
protected void configureWholeDayPane() {
wholeDayPane.getRowConstraints().add(new RowConstraints(10, 150, Control.USE_COMPUTED_SIZE, Priority.ALWAYS, VPos.TOP, true));
createTimeAndDayColumns(wholeDayPane);
createSeparators(wholeDayPane);
for (int i = 0; i < 7; i++) {
VBox cell = createWholeDayCell(i);
wholeDayCells.put(i, cell);
GridPane.setMargin(cell, new Insets(0, INSETS_WHOLEDAY, 0, INSETS_WHOLEDAY));
wholeDayPane.add(cell, i + 1, 0);
}
}
protected void createSeparators(GridPane pane) {
for (int i = 0; i < 7; i++) {
Separator separator = new Separator();
separator.setOrientation(Orientation.VERTICAL);
separator.setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
pane.add(separator, i + 1, 0, 1, Integer.MAX_VALUE);
GridPane.setHalignment(separator, HPos.LEFT);
}
}
protected void createTimeAndDayColumns(GridPane pane) {
ReadOnlyDoubleProperty width = widthProperty();
pane.prefWidthProperty().bind(width);
pane.minWidthProperty().bind(width);
pane.maxWidthProperty().bind(width);
ColumnConstraints timeColumn = new ColumnConstraints(WIDTH_OF_TIMECOLUMN, WIDTH_OF_TIMECOLUMN, Control.USE_PREF_SIZE, Priority.NEVER, HPos.RIGHT, true);
timeColumn.setPercentWidth(PERCENT_WIDTH_TIME_COLUMN);
pane.getColumnConstraints().add(timeColumn);
for (int i = 0; i < 7; i++) {
ColumnConstraints constraints = new ColumnConstraints(10, 80, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true);
constraints.setPercentWidth(PERCENT_WIDTH_DAY_COLUMN);
pane.getColumnConstraints().add(constraints);
}
}
protected VBox createWholeDayCell(int day) {
String cellStyle = "week-day-cell";
VBox cell = new VBox();
cell.setPadding(new Insets(5, 0, 5, 0));
cell.setSpacing(5);
cell.getStyleClass().add(cellStyle);
cell.getStyleClass().add("week-day-cell");
cell.setOnMouseClicked(e -> {
LocalDate firstDayOfWeek = getFirstDayOfWeek();
LocalDate selectedDay = firstDayOfWeek.plusDays(day);
BiConsumer<LocalDate, LocalTime> consumer = onAppointmentCreation.get();
if (consumer != null) {
consumer.accept(selectedDay, null);
}
});
Predicate<DragEvent> filter = e -> {
Object content = e.getDragboard().getContent(getDataFormat());
if (content != null) {
String title = (String) e.getDragboard().getContent(getDataFormat());
Optional<WeekViewAppointment<T>> first = entries.stream().filter(entry -> entry.getTitle().equals(title)).findFirst();
if (first.isPresent()) {
WeekViewAppointment<?> weekViewAppointment = first.get();
LocalDate newDate = getNewAppointmentDate(weekViewAppointment, day);
if (weekViewAppointment.getNewTimePossiblePredicate().test(newDate, null)) {
return true;
}
}
}
return false;
};
cell.setOnDragOver(e -> {
if (filter.test(e)) {
e.acceptTransferModes(TransferMode.MOVE);
e.consume();
}
});
cell.setOnDragEntered(e -> {
if (filter.test(e)) {
cell.getStyleClass().add("week-day-cell-drag");
e.consume();
}
});
cell.setOnDragExited(e -> {
if (filter.test(e)) {
cell.getStyleClass().remove("week-day-cell-drag");
e.consume();
}
});
cell.setOnDragDropped(e -> {
if (filter.test(e)) {
String title = (String) e.getDragboard().getContent(getDataFormat());
Optional<WeekViewAppointment<T>> first = entries.stream().filter(entry -> entry.getTitle().equals(title)).findFirst();
if (first.isPresent()) {
WeekViewAppointment weekViewAppointment = first.get();
weekViewAppointment.getControl().setVisible(true);
LocalDate newDate = getNewAppointmentDate(weekViewAppointment, day);
weekViewAppointment.setStart(newDate, null);
ArrayList<WeekViewAppointment<T>> copyOfEntries = new ArrayList<>(entries);
recreateEntries(copyOfEntries);
}
e.consume();
}
});
return cell;
}
protected StackPane createHourCell(int row, int column) {
String cellStyle = row % 2 == 0 ? "week-bg-even" : "week-bg-odd";
StackPane cell = new StackPane();
cell.getStyleClass().add(cellStyle);
if (column > 0) {
cell.getStyleClass().add("week-cell");
final int day = column - 1;
final int hour = row;
cell.setOnMouseClicked(e -> {
LocalDate firstDayOfWeek = getFirstDayOfWeek();
LocalDate selectedDay = firstDayOfWeek.plusDays(day);
LocalDateTime creationTime = LocalDateTime.of(selectedDay, LocalTime.of(hour, 0));
BiConsumer<LocalDate, LocalTime> consumer = onAppointmentCreation.get();
if (consumer != null) {
consumer.accept(creationTime.toLocalDate(), creationTime.toLocalTime());
}
});
Predicate<DragEvent> filter = e -> {
Object content = e.getDragboard().getContent(getDataFormat());
if (content != null) {
String title = (String) e.getDragboard().getContent(getDataFormat());
Optional<WeekViewAppointment<T>> first = entries.stream().filter(entry -> entry.getTitle().equals(title)).findFirst();
if (first.isPresent()) {
WeekViewAppointment<?> weekViewAppointment = first.get();
int minute = (int) ((e.getY() % HEIGHT_OF_HOUR) / 15) * 15;
minute = Math.max(0, minute);
LocalDateTime newTime = getNewAppointmentTime(weekViewAppointment, day, hour, minute);
if (weekViewAppointment.getNewTimePossiblePredicate().test(newTime.toLocalDate(), newTime.toLocalTime())) {
return true;
}
}
}
return false;
};
cell.setOnDragOver(e -> {
if (filter.test(e)) {
e.acceptTransferModes(TransferMode.MOVE);
e.consume();
}
});
cell.setOnDragEntered(e -> {
if (filter.test(e)) {
cell.getStyleClass().add("week-cell-drag");
e.consume();
}
});
cell.setOnDragExited(e -> {
if (filter.test(e)) {
cell.getStyleClass().remove("week-cell-drag");
e.consume();
}
});
cell.setOnDragDropped(e -> {
if (filter.test(e)) {
String title = (String) e.getDragboard().getContent(getDataFormat());
Optional<WeekViewAppointment<T>> first = entries.stream().filter(entry -> entry.getTitle().equals(title)).findFirst();
if (first.isPresent()) {
WeekViewAppointment weekViewAppointment = first.get();
weekViewAppointment.getControl().setVisible(true);
double percentagePos = 100D / hourHeight.get() * e.getY();
int minute = (int) (60D / 100D * percentagePos);
int grid = (int) (15F / (hourHeight.get() / 60));
minute -= minute % grid;
LocalDateTime newTime = getNewAppointmentTime(weekViewAppointment, day, hour, minute);
weekViewAppointment.setStart(newTime.toLocalDate(), newTime.toLocalTime());
ArrayList<WeekViewAppointment<T>> copyOfEntries = new ArrayList<>(entries);
recreateEntries(copyOfEntries);
}
e.consume();
}
});
}
return cell;
}
protected void entriesChanged(ListChangeListener.Change<? extends WeekViewAppointment<T>> change) {
while (change.next()) {
change.getRemoved().forEach(e -> {
Control control = e.getControl();
for (int i = 0; i < 9; i++) {
control.getStyleClass().remove("week-entry" + i);
}
contentPane.getChildren().remove(control);
wholeDayCells.values().forEach(vbox -> vbox.getChildren().remove(control));
});
LocalDate firstDayOfWeek = helper.getFirstDayOfWeek(year.getValue(), weekOfYear.getValue());
change.getAddedSubList().forEach(appointment -> {
long between = ChronoUnit.DAYS.between(firstDayOfWeek, appointment.getStartDate());
if (between >= 0 && between < 7) {
Control node = appointment.getControl();
applyStyle(appointment, node);
node.setOnDragDetected(event -> {
startAppointmentDragInternal(appointment);
event.consume();
});
node.focusedProperty().addListener((p, o, n) -> {
if (n) {
node.toFront();
}
});
if (appointment.isSpanningWholeDay()) {
VBox vbox = wholeDayCells.get((int) between);
vbox.getChildren().add(node);
node.setPrefHeight(Control.USE_COMPUTED_SIZE);
VBox.setMargin(node, new Insets(0, 0, 0, INSETS_WHOLEDAY));
} else {
long hours = ChronoUnit.HOURS.between(LocalTime.of(0, 0), appointment.getStartTime());
double percentageOfHourStartMinute = 100D / 60D * appointment.getStart().getMinute();
long durationInMinutes = appointment.getDuration().toMinutes();
if (durationInMinutes == 0) {
durationInMinutes = 15;
}
double percentageOfHourDuration = 100D / 60D * durationInMinutes;
double insetsTop = hourHeight.get() / 100 * percentageOfHourStartMinute;
double height = hourHeight.get() / 100 * percentageOfHourDuration;
node.setPrefHeight(height);
node.setMinHeight(Control.USE_PREF_SIZE);
node.setMaxHeight(Control.USE_PREF_SIZE);
contentPane.add(node, (int) between + 1, (int) hours, 1, Integer.MAX_VALUE);
GridPane.setMargin(node, new Insets(1 + insetsTop, 0, 0, 2));
}
}
});
}
}
public void startAppointmentDrag(WeekViewAppointment<T> appointment) {
if (appointment.getStartDate().isAfter(getLastDayOfWeek()) || appointment.getStartDate().isBefore(getFirstDayOfWeek())) {
appointment.setStartWithoutCallback(getFirstDayOfWeek(), appointment.getStartTime());
entries.remove(appointment);
}
if (!entries.contains(appointment)) {
entries.add(appointment);
appointment.getControl().setVisible(false);
} else {
int i = entries.indexOf(appointment);
appointment = entries.get(i);
}
startAppointmentDragInternal(appointment);
}
protected void startAppointmentDragInternal(WeekViewAppointment<T> appointment) {
Control source = appointment.getControl();
if (appointment.getChangeStartCallback() == null) {
return;
}
Dragboard dragboard = source.startDragAndDrop(TransferMode.MOVE);
dragboard.clear();
Map<DataFormat, Object> content = new HashMap<>();
DataFormat dataFormat = getDataFormat();
content.put(dataFormat, appointment.getTitle());
dragboard.setContent(content);
checkCreateDragView(source, dragboard);
}
protected void checkCreateDragView(Control source, Dragboard dragboard) {
boolean visible = source.isVisible();
if (!visible) {
source.setVisible(true);
}
if (source.getWidth() > 0 && source.getHeight() > 0 && dragboard.getDragView() == null) {
WritableImage image = new WritableImage((int) source.getWidth(), (int) source.getHeight());
SnapshotParameters params = new SnapshotParameters();
Image snapshot = source.snapshot(params, image);
dragboard.setDragView(snapshot);
}
if (!visible) {
source.setVisible(false);
}
}
protected void applyStyle(WeekViewAppointment appointment, Control node) {
if (!node.getStyleClass().contains("week-entry")) {
node.getStyleClass().add("week-entry");
}
if (appointment.styleClass == null) {
appointment.styleClass = "week-entry" + currentEntryStyleNr;
currentEntryStyleNr++;
if (currentEntryStyleNr == 9) {
currentEntryStyleNr = 1;
}
}
if (!node.getStyleClass().contains(appointment.styleClass)) {
node.getStyleClass().add(appointment.styleClass);
}
}
public void recreateEntries() {
if (appointmentResolver.get() == null) {
return;
}
currentEntryStyleNr = 0;
LocalDate firstDayOfWeek = helper.getFirstDayOfWeek(year.getValue(), weekOfYear.getValue());
LocalDate lastDayOfWeek = helper.getLastDayOfWeek(year.getValue(), weekOfYear.getValue());
AppointmentResolver<T> resolver = appointmentResolver.get();
resolver.resolve(firstDayOfWeek, lastDayOfWeek, this::recreateEntries);
}
public void recreateEntries(List<? extends WeekViewAppointment<T>> weekViewAppointments) {
Collections.sort(weekViewAppointments);
entries.clear();
entries.addAll(weekViewAppointments);
}
public DataFormat getDataFormat() {
DataFormat dataFormat = DataFormat.lookupMimeType(WeekViewAppointment.class.getName());
if (dataFormat == null) {
dataFormat = new DataFormat(WeekViewAppointment.class.getName());
}
return dataFormat;
}
protected LocalDateTime getNewAppointmentTime(WeekViewAppointment appointment, int newDay, int newHour, int minutes) {
LocalDate newDate = getNewAppointmentDate(appointment, newDay);
LocalTime newTime = LocalTime.of(newHour, minutes);
return LocalDateTime.of(newDate, newTime);
}
protected LocalDate getNewAppointmentDate(WeekViewAppointment appointment, int newDay) {
LocalDate start = appointment.getStartDate();
int originalDayOfWeek = start.getDayOfWeek().getValue();
int selectedDayOfWeek = newDay + 1;
if (originalDayOfWeek > selectedDayOfWeek) {
start = start.minusDays(originalDayOfWeek - selectedDayOfWeek);
} else if (originalDayOfWeek < selectedDayOfWeek) {
start = start.plusDays(selectedDayOfWeek - originalDayOfWeek);
}
return start;
}
protected void recompute() {
if (recomupting) {
return;
} else {
recomupting = true;
}
int isoYear = year.get();
int weeksInYear = helper.getWeeksInYear(isoYear);
if (weekOfYear.getValue() == 0) {
int previousYear = isoYear - 1;
weekOfYear.set(helper.getWeeksInYear(previousYear));
year.set(previousYear);
} else if (weekOfYear.getValue() > weeksInYear) {
weekOfYear.set(1);
year.set(year.getValue() + 1);
}
updateWeekDays();
recomupting = false;
recreateEntries();
}
protected void updateWeekDays() {
LocalDate currentDay = helper.getFirstDayOfWeek(year.getValue(), weekOfYear.getValue());
for (int i = 0; i < 7; i++) {
Label label = weekDayLabels.get(i);
DayOfWeek dayOfWeek = currentDay.getDayOfWeek();
label.setText(dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + ". " + currentDay.getDayOfMonth());
for (int hours = 0; hours < 24; hours++) {
ObservableList<String> styleClass = cells.get(hours, i + 1).getStyleClass();
styleClass.remove("week-bg-even-today");
styleClass.remove("week-bg-odd-today");
}
if (currentDay.equals(LocalDate.now())) {
for (int hours = 0; hours < 24; hours++) {
ObservableList<String> styleClass = cells.get(hours, i + 1).getStyleClass();
String cellStyle = hours % 2 == 0 ? "week-bg-even-today" : "week-bg-odd-today";
styleClass.add(cellStyle);
}
}
currentDay = currentDay.plusDays(1);
}
}
public int getYear() {
return year.get();
}
public SimpleIntegerProperty yearProperty() {
return year;
}
public void setYear(int year) {
this.year.set(year);
}
public int getWeekOfYear() {
return weekOfYear.get();
}
public SimpleIntegerProperty weekOfYearProperty() {
return weekOfYear;
}
public void setWeekOfYear(int weekOfYear) {
this.weekOfYear.set(weekOfYear);
}
public ObservableList<WeekViewAppointment<T>> getEntries() {
return entries;
}
public ScrollPane getContentScollPane() {
return contentScollPane;
}
public LocalDate getFirstDayOfWeek() {
return helper.getFirstDayOfWeek(year.getValue(), weekOfYear.getValue());
}
public LocalDate getLastDayOfWeek() {
return helper.getLastDayOfWeek(year.getValue(), weekOfYear.getValue());
}
public BiConsumer<LocalDate, LocalTime> getOnAppointmentCreation() {
return onAppointmentCreation.get();
}
public ObjectProperty<BiConsumer<LocalDate, LocalTime>> onAppointmentCreationProperty() {
return onAppointmentCreation;
}
public void setOnAppointmentCreation(BiConsumer<LocalDate, LocalTime> onAppointmentCreation) {
this.onAppointmentCreation.set(onAppointmentCreation);
}
public Table<Integer, Integer, StackPane> getCells() {
return cells;
}
public Button getTodayButton() {
return title.today;
}
public AppointmentResolver getAppointmentResolver() {
return appointmentResolver.get();
}
public ObjectProperty<AppointmentResolver<T>> appointmentResolverProperty() {
return appointmentResolver;
}
public void setAppointmentResolver(AppointmentResolver<T> appointmentResolver) {
this.appointmentResolver.set(appointmentResolver);
}
}