/*
* 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.idnadrev.task.view;
import de.ks.BaseController;
import de.ks.i18n.Localized;
import de.ks.idnadrev.entity.Task;
import de.ks.idnadrev.entity.WorkUnit;
import de.ks.persistence.PersistentWork;
import de.ks.validation.ValidationMessage;
import de.ks.validation.validators.NotEmptyValidator;
import de.ks.validation.validators.TimeHHMMValidator;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.Validator;
import java.net.URL;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;
public class WorkUnitController extends BaseController<List<Task>> {
@FXML
protected TableView<WorkUnit> workUnitTable;
@FXML
protected TableColumn<WorkUnit, String> endColumn;
@FXML
protected TableColumn<WorkUnit, String> startColumn;
@FXML
protected TableColumn<WorkUnit, String> duration;
@FXML
protected DatePicker date;
@FXML
protected TextField start;
@FXML
protected Button edit;
@FXML
protected Button createNew;
@FXML
protected Button delete;
@FXML
protected TextField end;
@FXML
protected GridPane root;
protected final SimpleObjectProperty<Task> task = new SimpleObjectProperty<>();
protected final SimpleBooleanProperty createNewPossible = new SimpleBooleanProperty();
protected final DateTimeFormatter fullDateTimeFormatter = DateTimeFormatter.ofPattern(Localized.get("fullDate"));
protected final DateTimeFormatter hoursMinutesFormatter = DateTimeFormatter.ofPattern(Localized.get("duration.format"));
@Override
public void initialize(URL location, ResourceBundle resources) {
startColumn.setCellValueFactory(param -> createTimeStringProperty(param.getValue().getStart()));
endColumn.setCellValueFactory(param -> createTimeStringProperty(param.getValue().getEnd()));
duration.setCellValueFactory(param -> {
SimpleStringProperty retval = new SimpleStringProperty();
long min = param.getValue().getDuration().toMinutes();
retval.set(min + "m");
return retval;
});
task.addListener((p, o, n) -> {
workUnitTable.getItems().clear();
if (n != null) {
reload(n);
}
});
ReadOnlyObjectProperty<WorkUnit> selectedItemProperty = workUnitTable.getSelectionModel().selectedItemProperty();
selectedItemProperty.addListener((p, o, n) -> {
if (n == null) {
end.setText("");
start.setText("");
date.setValue(LocalDate.now());
} else {
start.setText(hoursMinutesFormatter.format(n.getStart()));
end.setText(hoursMinutesFormatter.format(n.getEnd()));
date.setValue(n.getStart().toLocalDate());
}
});
validationRegistry.registerValidator(start, new TimeHHMMValidator());
validationRegistry.registerValidator(end, new TimeHHMMValidator());
validationRegistry.registerValidator(start, new NotEmptyValidator());
validationRegistry.registerValidator(end, new NotEmptyValidator());
Validator<Object> validator = (control, value) -> {
try {
LocalTime startTime = LocalTime.parse(start.getText(), hoursMinutesFormatter);
LocalTime endTime = LocalTime.parse(end.getText(), hoursMinutesFormatter);
if (startTime.isAfter(endTime)) {
return ValidationResult.fromMessages(new ValidationMessage("validation.time.before", control, startTime, endTime));
}
return null;
} catch (DateTimeParseException e) {
return null;
}
};
// validationRegistry.registerValidator(start, validator);
validationRegistry.registerValidator(end, validator);
date.setValue(LocalDate.now());
edit.disableProperty().bind(validationRegistry.invalidProperty().or(selectedItemProperty.isNull()));
createNew.disableProperty().bind(validationRegistry.invalidProperty().or(createNewPossible.not()));
delete.disableProperty().bind(selectedItemProperty.isNull().or(store.loadingProperty()));
start.textProperty().addListener((p, o, n) -> {
if (n != null && validationRegistry.isValid()) {
checkStartTimeValidForNew();
} else {
try {
LocalDateTime startTime = getEnteredDate(n);
LocalDateTime endTime = getEnteredDate(end.getText());
if (endTime.isBefore(startTime)) {
String endText = hoursMinutesFormatter.format(startTime.plusMinutes(1));
end.setText(endText);
}
} catch (DateTimeParseException e) {
//
}
createNewPossible.set(false);
controller.getExecutorService().schedule(() -> {
controller.getJavaFXExecutor().submit(() -> checkStartTimeValidForNew());
}, 100, TimeUnit.MILLISECONDS);
}
});
}
protected void reload(Task n) {
workUnitTable.getItems().clear();
PersistentWork.wrap(() -> {
Task reload = PersistentWork.reload(n);
workUnitTable.getItems().addAll(reload.getWorkUnits());
});
}
protected void checkStartTimeValidForNew() {
try {
LocalDateTime startTime = getEnteredDate(start.getText());
LocalDateTime endTime = getEnteredDate(end.getText());
ObservableList<WorkUnit> items = workUnitTable.getItems();
boolean isNewStartTime = checkNewStartTimePossible(startTime, endTime, items);
createNewPossible.set(isNewStartTime);
} catch (DateTimeParseException e) {
createNewPossible.set(false);
}
}
protected boolean checkNewStartTimePossible(LocalDateTime startTime, LocalDateTime endTime, List<WorkUnit> items) {
for (WorkUnit workUnit : items) {
LocalDateTime currentStart = workUnit.getStart().withSecond(0).withNano(0);
LocalDateTime currentEnd = workUnit.getEnd().withSecond(0).withNano(0);
if (startTime.isEqual(currentStart) || startTime.isEqual(currentEnd)) {
return false;
} else if (startTime.isAfter(currentStart) && startTime.isBefore(currentEnd)) {
return false;
} else if (endTime.isAfter(currentStart) && endTime.isBefore(currentEnd)) {
return false;
} else if (currentStart.isAfter(startTime) && currentStart.isBefore(endTime)) {
return false;
}
}
return true;
}
protected LocalDateTime getEnteredDate(String text) {
LocalTime time = LocalTime.parse(text, hoursMinutesFormatter);
return LocalDateTime.of(date.getValue(), time);
}
protected SimpleStringProperty createTimeStringProperty(LocalDateTime dateTime) {
SimpleStringProperty retval = new SimpleStringProperty();
if (dateTime != null) {
retval.set(fullDateTimeFormatter.format(dateTime));
}
return retval;
}
public GridPane getRoot() {
return root;
}
public Task getTask() {
return task.get();
}
public SimpleObjectProperty<Task> taskProperty() {
return task;
}
public void setTask(Task task) {
this.task.set(task);
}
@FXML
public void onEdit() {
WorkUnit selectedItem = workUnitTable.getSelectionModel().getSelectedItem();
LocalDateTime endTime = getEnteredDate(end.getText());
LocalDateTime startTime = getEnteredDate(start.getText());
store.executeCustomRunnable(() -> {
PersistentWork.wrap(() -> {
WorkUnit reload = PersistentWork.reload(selectedItem);
reload.setStart(startTime);
reload.setEnd(endTime);
});
controller.getJavaFXExecutor().submit(() -> reload(selectedItem.getTask()));
});
}
@FXML
public void onDelete() {
WorkUnit selectedItem = workUnitTable.getSelectionModel().getSelectedItem();
store.executeCustomRunnable(() -> {
PersistentWork.run(em -> {
WorkUnit reload = PersistentWork.reload(selectedItem);
em.remove(reload);
});
controller.getJavaFXExecutor().submit(() -> reload(task.get()));
});
}
@FXML
public void onCreateNew() {
Task currentTask = this.task.get();
LocalDateTime endTime = getEnteredDate(end.getText());
LocalDateTime startTime = getEnteredDate(start.getText());
store.executeCustomRunnable(() -> {
PersistentWork.wrap(() -> {
Task reload = PersistentWork.reload(currentTask);
WorkUnit workUnit = new WorkUnit(reload);
workUnit.setStart(startTime);
workUnit.setEnd(endTime);
controller.getJavaFXExecutor().submit(() -> reload(currentTask));
});
});
}
}