Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package mesclasses.view; import com.google.common.eventbus.Subscribe; import java.io.IOException; import java.net.URL; import java.time.DayOfWeek; import java.time.LocalDate; import java.util.List; import java.util.ResourceBundle; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.RowConstraints; import javafx.scene.layout.VBox; import javafx.scene.text.TextFlow; import javafx.stage.Modality; import javafx.stage.Stage; import mesclasses.MainApp; import mesclasses.controller.PageController; import mesclasses.handlers.EventBusHandler; import mesclasses.model.Constants; import mesclasses.model.Cours; import mesclasses.model.Devoir; import mesclasses.model.Eleve; import mesclasses.model.EleveData; import mesclasses.model.Journee; import mesclasses.model.Mot; import mesclasses.model.Seance; import mesclasses.model.Trimestre; import mesclasses.objects.IntegerOnlyConverter; import mesclasses.objects.TunedDayCellFactory; import mesclasses.objects.events.CreateCoursEvent; import mesclasses.objects.events.MessageEvent; import mesclasses.objects.events.OpenMenuEvent; import mesclasses.objects.events.SelectClasseEvent; import mesclasses.objects.events.SelectDateEvent; import mesclasses.objects.events.SelectSeanceEvent; import mesclasses.util.Btns; import mesclasses.util.CssUtil; import mesclasses.util.LogUtil; import mesclasses.util.ModalUtil; import mesclasses.util.NodeUtil; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.smartgrid.SmartGrid; import org.smartselect.SmartSelect; /** * FXML Controller class * * @author rrrt3491 */ public class JourneeController extends PageController implements Initializable { private static final Logger LOG = LogManager.getLogger(JourneeController.class); // FXML elements @FXML TabPane tabPane; @FXML Tab vieScolaireTab; @FXML Tab travailTab; @FXML Tab sanctionsTab; @FXML SmartGrid vieScolaireGrid; @FXML SmartGrid travailGrid; @FXML SmartGrid sanctionsGrid; @FXML Label trimestreName; @FXML DatePicker currentDate; @FXML private SmartSelect<Seance> seanceSelect; @FXML Button remCoursBtn; @FXML Button addCoursBtn; @FXML Button previousDayBtn; @FXML Button nextDayBtn; @FXML Button switchNonActifsBtn; @FXML Button rapportsBtn; @FXML Button punitionsBtn; @FXML Button postItBtn; @FXML Button ramasserBtn; // fields private Trimestre trimestre; private Journee journee; private ScrollPane selectedScroll; private SmartGrid selectedGrid; // bindings BooleanBinding isFirstCours; BooleanBinding isLastCours; BooleanProperty displayNonActifs; private final ChangeListener<LocalDate> dateListener = (observable, oldValue, newValue) -> { LOG.debug("Date change detected --> setting selectSeance"); setCurrentDateAndSeance(newValue, null); }; /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { LogUtil.logStart(); name = "Journee Ctrl"; super.initialize(url, rb); LOG.debug("Initialisation de " + name); initTabs(); Btns.makeLeft(previousDayBtn); Btns.makeRight(nextDayBtn); Btns.makeLeft(seanceSelect.getBtnLeft()); Btns.makeRight(seanceSelect.getBtnRight()); Btns.makeAdd(addCoursBtn); Btns.makeDelete(remCoursBtn); Btns.tooltip(previousDayBtn, "Jour prcdent"); Btns.tooltip(nextDayBtn, "Jour suivant"); Btns.tooltip(seanceSelect.getBtnLeft(), "Sance prcdente"); Btns.tooltip(seanceSelect.getBtnRight(), "Sance suivante"); Btns.tooltip(addCoursBtn, "Ajouter une sance ponctuelle"); Btns.tooltip(remCoursBtn, "Supprimer la sance ponctuelle"); Btns.tooltip(switchNonActifsBtn, "active/dsactive l'affichage des lves inactifs"); Btns.tooltip(rapportsBtn, "Ouvre la page des rapports trimestriels pour la classe"); Btns.tooltip(punitionsBtn, "Ouvre le suivi des punitions, devoirs et mots pour la classe"); Btns.tooltip(postItBtn, "ouvre le postIt de la classe"); Btns.tooltip(ramasserBtn, "ouvre la fentre des actions faire pour la classe"); currentDate.setDayCellFactory(new TunedDayCellFactory().setAllowSundays(false)); currentDate.setConverter(NodeUtil.DATE_WITH_DAY_NAME); currentDate.valueProperty().addListener(dateListener); displayNonActifs = new SimpleBooleanProperty(false); switchNonActifsBtn.setText("Afficher inactifs"); displayNonActifs.addListener((observable, oldValue, newValue) -> { LOG.debug("Switch actifs/inactifs"); switchNonActifsBtn.setText(displayNonActifs.get() ? "Masquer inactifs" : "Afficher inactifs"); refreshGrid(); }); seanceSelect.addChangeListener((ob, o, n) -> { LOG.debug("seanceSelect changed --> reloading"); loadCurrentSeance(); }); // activation du bouton remCours si besoin rapportsBtn.disableProperty().bind(Bindings.isNull(seanceSelect.valueProperty())); punitionsBtn.disableProperty().bind(Bindings.isNull(seanceSelect.valueProperty())); postItBtn.disableProperty().bind(Bindings.isNull(seanceSelect.valueProperty())); // on ne change que la date, le listener sur currentDate se charge du reste LocalDate today = LocalDate.now(); if (today.getDayOfWeek().equals(DayOfWeek.SUNDAY)) { today = today.minusDays(1); } LOG.debug("Initializing with today's date"); currentDate.setValue(today); LogUtil.logEnd(); } private void initTabs() { initTab(vieScolaireTab, vieScolaireGrid); initTab(travailTab, travailGrid); initTab(sanctionsTab, sanctionsGrid); tabPane.getSelectionModel().clearAndSelect(0); selectTab(vieScolaireTab, vieScolaireGrid); } private void initTab(Tab tab, SmartGrid grid) { tab.getContent().setManaged(false); tab.getContent().setVisible(false); tab.selectedProperty().addListener((ob, o, n) -> { if (n) { selectTab(tab, grid); } else { unselectTab(tab); } }); } private void selectTab(Tab tab, SmartGrid grid) { tab.getContent().setManaged(true); tab.getContent().setVisible(true); double vvalue = 0; if (selectedScroll != null) { vvalue = selectedScroll.getVvalue(); } selectedGrid = grid; selectedScroll = getScrollPane(tab); selectedScroll.setVvalue(vvalue); } private ScrollPane getScrollPane(Tab tab) { VBox box = (VBox) tab.getContent(); for (Node child : box.getChildren()) { if (child instanceof ScrollPane) { return (ScrollPane) child; } } return null; } private void unselectTab(Tab tab) { tab.getContent().setManaged(false); tab.getContent().setVisible(false); } public void setCurrentDateAndSeance(LocalDate date, Seance seance) { LogUtil.logStart(); // on coupe le listener sur la date pour viter les appels en boucle currentDate.valueProperty().removeListener(dateListener); LOG.debug("Setting the date silently"); currentDate.setValue(date); // on rtablit le listener une fois la date change currentDate.valueProperty().addListener(dateListener); journee = model.getJournee(date); if (seance != null) { LOG.debug("Chargement des sances et slection de la sance " + seance); seanceSelect.setItems(journee.getSeances(), false); // will trigger the refreshGrid seanceSelect.select(seance); } else { LOG.debug("Chargement des sances. nb de sances = " + journee.getSeances().size()); // will trigger the refreshGrid seanceSelect.setItems(journee.getSeances(), true); if (journee.getSeances().isEmpty()) { // on force le refresh de la grid loadCurrentSeance(); } } LogUtil.logEnd(); } public void loadCurrentSeance() { LogUtil.logStart(); remCoursBtn.setDisable(seanceSelect.getValue() == null || !seanceSelect.getValue().getCours().isPonctuel()); // reinitialisation du scroll vertical getScrollPane(tabPane.getSelectionModel().getSelectedItem()).setVvalue(0.0); if (seanceSelect.getValue() != null && seanceSelect.getValue().getClasse() != null) { seanceSelect.getValue().getClasse().postItProperty().addListener((ob, o, n) -> { setPostItColor(); }); setPostItColor(); ramasserBtn.setVisible(model.hasActionsEnCours(seanceSelect.getValue().getClasse())); } else { ramasserBtn.setVisible(false); } refreshGrid(); LogUtil.logEnd(); } private void setPostItColor() { if (!StringUtils.isEmpty(seanceSelect.getValue().getClasse().getPostIt())) { CssUtil.addClass(postItBtn, "button-delete"); CssUtil.removeClass(postItBtn, "button-warning"); } else { CssUtil.addClass(postItBtn, "button-warning"); CssUtil.removeClass(postItBtn, "button-delete"); } } @Subscribe public void onReceiveSelectSeanceEvent(SelectSeanceEvent e) { logEvent(e); setCurrentDateAndSeance(e.getSeance().getDateAsDate(), e.getSeance()); } @Subscribe public void onReceiveSelectDateEvent(SelectDateEvent e) { logEvent(e); setCurrentDateAndSeance(e.getDate(), null); } private void refreshGrid() { LogUtil.logStart(); LOG.debug("Refreshing the grid for " + name); trimestre = model.getForDate(currentDate.getValue()); if (trimestre == null) { vieScolaireGrid.drawNoTrimestreAlert(currentDate.getValue(), (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.ADMIN_TRIMESTRE_VIEW)); }); travailGrid.drawNoTrimestreAlert(currentDate.getValue(), (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.ADMIN_TRIMESTRE_VIEW)); }); sanctionsGrid.drawNoTrimestreAlert(currentDate.getValue(), (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.ADMIN_TRIMESTRE_VIEW)); }); addCoursBtn.setDisable(true); trimestreName.setText(""); return; } addCoursBtn.setDisable(false); trimestreName.setText(trimestre.getName()); if (seanceSelect.getValue() != null) { if (seanceSelect.getValue().getClasse().getEleves().isEmpty()) { vieScolaireGrid.drawNoEleveAlert(seanceSelect.getValue().getClasse().getName(), (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.ADMIN_ELEVE_VIEW)); }); travailGrid.drawNoEleveAlert(seanceSelect.getValue().getClasse().getName(), (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.ADMIN_ELEVE_VIEW)); }); sanctionsGrid.drawNoEleveAlert(seanceSelect.getValue().getClasse().getName(), (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.ADMIN_ELEVE_VIEW)); }); addCoursBtn.setDisable(true); return; } trimestre = model.getForDate(currentDate.getValue()); drawGrid(); } else { addCoursBtn.setDisable(false); String day = Constants.DAYMAP.get(currentDate.getValue().getDayOfWeek()); vieScolaireGrid.drawNoSeanceAlert(day, (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.EMPLOI_DU_TEMPS_VIEW)); EventBusHandler.post(new CreateCoursEvent(day, null)); }); travailGrid.drawNoSeanceAlert(day, (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.EMPLOI_DU_TEMPS_VIEW)); EventBusHandler.post(new CreateCoursEvent(day, null)); }); sanctionsGrid.drawNoSeanceAlert(day, (event) -> { EventBusHandler.post(new OpenMenuEvent(Constants.EMPLOI_DU_TEMPS_VIEW)); EventBusHandler.post(new CreateCoursEvent(day, null)); }); } LogUtil.logEnd(); } @FXML public void previousDay() { if (currentDate.getValue().getDayOfWeek() == DayOfWeek.MONDAY) { setCurrentDateAndSeance(currentDate.getValue().minusDays(2), null); } else { setCurrentDateAndSeance(currentDate.getValue().minusDays(1), null); } } @FXML public void nextDay() { if (currentDate.getValue().getDayOfWeek() == DayOfWeek.SATURDAY) { setCurrentDateAndSeance(currentDate.getValue().plusDays(2), null); } else { setCurrentDateAndSeance(currentDate.getValue().plusDays(1), null); } } @FXML public void ajouterCours() throws IOException { //creation d'un cours ponctuel Cours coursPonctuel = new Cours(); coursPonctuel.setPonctuel(true); coursPonctuel.setDay(NodeUtil.getJour(currentDate.getValue())); coursPonctuel.setWeek("ponctuel (" + currentDate.getValue().format(Constants.DATE_FORMATTER_FR) + ")"); coursPonctuel.setStartHour(16); coursPonctuel.setStartMin(00); coursPonctuel.setEndHour(17); coursPonctuel.setEndMin(00); coursPonctuel = openCoursDialog(coursPonctuel); if (coursPonctuel == null) { return; } Seance newSeance = model.addSeanceWithCoursPonctuel(journee, coursPonctuel); seanceSelect.select(newSeance); } @FXML public void supprimerCours() throws IOException { StringBuilder confirmMsg = new StringBuilder("Supprimer la sance ponctuelle de ") .append(NodeUtil.getStartTime(seanceSelect.getValue().getCours())).append(" avec la ") .append(seanceSelect.getValue().getClasse().getName()); if (ModalUtil.confirm("Suppression de sance", confirmMsg.toString())) { journee.getCoursPonctuels().remove(seanceSelect.getValue().getCours()); journee.getSeances().remove(seanceSelect.getValue()); refreshGrid(); } } private Cours openCoursDialog(Cours cours) { try { FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource(Constants.COURS_EDIT_DIALOG)); AnchorPane page = (AnchorPane) loader.load(); // Create the dialog Stage. Stage dialogStage = new Stage(); dialogStage.setTitle("Cration d'un cours ponctuel"); dialogStage.initModality(Modality.WINDOW_MODAL); dialogStage.initOwner(primaryStage); Scene scene = new Scene(page); dialogStage.setScene(scene); // Set the person into the controller. CoursEditDialogController controller = loader.getController(); controller.setDialogStage(dialogStage); controller.setCours(cours, true); dialogStage.showAndWait(); int status = controller.getStatus(); if (status >= 0) { //update/cancel return controller.getCours(); } } catch (IOException e) { ModalUtil.alert("Erreur I/O", e.getMessage()); } return null; } /* BOTTOM BUTTONS */ @FXML public void switchNonActifs() { displayNonActifs.set(!displayNonActifs.get()); } @FXML public void openRapport() { EventBusHandler.post(new OpenMenuEvent(Constants.CLASSE_RAPPORT_TABS_VIEW)); EventBusHandler.post(new SelectClasseEvent(seanceSelect.getValue().getClasse())); } @FXML public void openPunitions() { EventBusHandler.post(new OpenMenuEvent(Constants.CLASSE_PUNITIONS_VIEW)); EventBusHandler.post(new SelectClasseEvent(seanceSelect.getValue().getClasse())); } @FXML public void openPostIt() { try { // Load the fxml file and create a new stage for the popup dialog. FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource(Constants.POSTIT_DIALOG)); AnchorPane page = (AnchorPane) loader.load(); // Create the dialog Stage. Stage dialogStage = new Stage(); dialogStage.setTitle("Post-It"); dialogStage.initModality(Modality.WINDOW_MODAL); dialogStage.initOwner(primaryStage); Scene scene = new Scene(page); dialogStage.setScene(scene); // Set the person into the controller. PostItDialogController controller = loader.getController(); controller.setDialogStage(dialogStage); controller.setClasse(seanceSelect.getValue().getClasse()); // Show the dialog and wait until the user closes it dialogStage.showAndWait(); } catch (IOException e) { LOG.error(e); } } @FXML public void openActions() { try { // Load the fxml file and create a new stage for the popup dialog. FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource(Constants.ACTIONS_DIALOG)); AnchorPane page = (AnchorPane) loader.load(); // Create the dialog Stage. Stage dialogStage = new Stage(); dialogStage.setTitle("Actions faire"); dialogStage.initModality(Modality.NONE); dialogStage.initOwner(primaryStage); Scene scene = new Scene(page); dialogStage.setScene(scene); // Set the person into the controller. ActionsEnCoursController controller = loader.getController(); controller.setDialogStage(dialogStage); controller.setClasse(seanceSelect.getValue().getClasse()); // Show the dialog and wait until the user closes it dialogStage.showAndWait(); } catch (IOException e) { LOG.error(e); } } public boolean openPunitionDialog(Eleve eleve) { String texte = ModalUtil.prompt("Punition pour " + eleve.getFirstName() + " " + eleve.getLastName(), ""); if (!StringUtils.isEmpty(texte)) { model.createPunition(eleve, seanceSelect.getValue(), texte); EventBusHandler.post(MessageEvent.success("Punition cre")); return true; } return false; } /* GRID */ private void drawGrid() { vieScolaireGrid.clear(); travailGrid.clear(); sanctionsGrid.clear(); List<Eleve> elevesToDisplay = seanceSelect.getValue().getClasse().getEleves(); if (!displayNonActifs.get()) { elevesToDisplay = model.getOnlyActive(elevesToDisplay); } LOG.debug("drawing the grid for " + name); LOG.debug("Donnees prsentes : " + seanceSelect.getValue().getDonnees()); for (int i = 0; i < elevesToDisplay.size(); i++) { drawRow(elevesToDisplay.get(i), i); } } private void drawEleveName(SmartGrid grid, Eleve eleve, int rowIndex) { //index debute 1, le slot 0,X est rserv pour un separator dans le header grid.add(NodeUtil.buildEleveLink(eleve, eleve.lastNameProperty(), Constants.JOURNEE_VIEW), 1, rowIndex, HPos.LEFT); grid.add(NodeUtil.buildEleveLink(eleve, eleve.firstNameProperty(), Constants.JOURNEE_VIEW), 2, rowIndex, HPos.LEFT); RowConstraints rc = new RowConstraints(); rc.setMinHeight(35.0); rc.setPrefHeight(35.0); rc.setMaxHeight(35.0); grid.getRowConstraints().add(rc); if (!eleve.isInClasse(currentDate.getValue())) { grid.add(new Label("ne faisait pas partie de la classe cette date"), 3, rowIndex, vieScolaireGrid.getGridWidth() - 3, 1); } } /** * dessine la grid vie scolaire * @param eleve * @param rowIndex */ private void drawVieScolaire(Eleve eleve, int rowIndex) { EleveData eleveData = seanceSelect.getValue().getDonnees().get(eleve); drawEleveName(vieScolaireGrid, eleve, rowIndex); if (!eleve.isInClasse(currentDate.getValue())) { return; } CheckBox box = new CheckBox(); Bindings.bindBidirectional(box.selectedProperty(), eleveData.absentProperty()); vieScolaireGrid.add(box, 3, rowIndex, null); TextField retardField = new TextField(); retardField.setMaxWidth(50); Bindings.bindBidirectional(retardField.textProperty(), eleveData.retardProperty(), new IntegerOnlyConverter()); markAsInteger(retardField); vieScolaireGrid.add(retardField, 4, rowIndex, HPos.CENTER); Label cumulRetard = new Label(); retardField.textProperty().addListener((observable, oldValue, newValue) -> { writeAndMarkInRed(cumulRetard, stats.getNbRetardsUntil(eleve, currentDate.getValue()), 3); }); writeAndMarkInRed(cumulRetard, stats.getNbRetardsUntil(eleve, currentDate.getValue()), 3); vieScolaireGrid.add(cumulRetard, 5, rowIndex, null); } /** * dessine la grid travail * @param eleve * @param rowIndex */ private void drawTravail(Eleve eleve, int rowIndex) { EleveData eleveData = seanceSelect.getValue().getDonnees().get(eleve); drawEleveName(travailGrid, eleve, rowIndex); if (!eleve.isInClasse(currentDate.getValue())) { return; } CheckBox travailBox = new CheckBox(); Bindings.bindBidirectional(travailBox.selectedProperty(), eleveData.travailPasFaitProperty()); travailGrid.add(travailBox, 3, rowIndex, null); Label cumulTravail = new Label(); travailBox.selectedProperty().addListener((observable, oldValue, newValue) -> { writeAndMarkInRed(cumulTravail, stats.getNbTravailUntil(eleve, currentDate.getValue()), 3); }); writeAndMarkInRed(cumulTravail, stats.getNbTravailUntil(eleve, currentDate.getValue()), 3); travailGrid.add(cumulTravail, 4, rowIndex, null); CheckBox devoirBox = new CheckBox(); devoirBox.setSelected(model.getDevoirForSeance(eleve, seanceSelect.getValue()) != null); devoirBox.selectedProperty().addListener((ob, o, checked) -> { Devoir devoir = model.getDevoirForSeance(eleve, seanceSelect.getValue()); if (checked && devoir == null) { model.createDevoir(eleve, seanceSelect.getValue()); } else if (devoir != null) { model.delete(devoir); } }); travailGrid.add(devoirBox, 5, rowIndex, null); TextField oubliMaterielField = new TextField(); Bindings.bindBidirectional(oubliMaterielField.textProperty(), eleveData.oubliMaterielProperty()); travailGrid.add(oubliMaterielField, 6, rowIndex, HPos.LEFT); Label cumulOubli = new Label(); oubliMaterielField.textProperty().addListener((observable, oldValue, newValue) -> { writeAndMarkInRed(cumulOubli, stats.getNbOublisUntil(eleve, currentDate.getValue()), 3); }); writeAndMarkInRed(cumulOubli, stats.getNbOublisUntil(eleve, currentDate.getValue()), 3); travailGrid.add(cumulOubli, 7, rowIndex, null); } /** * dessine la grid sanctions * @param eleve * @param rowIndex */ private void drawSanctions(Eleve eleve, int rowIndex) { EleveData eleveData = seanceSelect.getValue().getDonnees().get(eleve); drawEleveName(sanctionsGrid, eleve, rowIndex); if (!eleve.isInClasse(currentDate.getValue())) { return; } HBox punitionsBox = new HBox(); punitionsBox.setAlignment(Pos.CENTER_LEFT); TextFlow nbPunitions = new TextFlow(); Label nbPunitionLabel = new Label( "" + eleve.getPunitions().stream().filter(p -> p.getSeance() == seanceSelect.getValue()).count()); nbPunitionLabel.setPrefHeight(20); nbPunitions.getChildren().add(new Label(" (")); nbPunitions.getChildren().add(nbPunitionLabel); nbPunitions.getChildren().add(new Label(")")); nbPunitions.visibleProperty().bind(nbPunitionLabel.textProperty().isNotEqualTo("0")); nbPunitions.managedProperty().bind(nbPunitionLabel.textProperty().isNotEqualTo("0")); Button punitionBtn = Btns.punitionBtn(); punitionBtn.setOnAction((event) -> { if (openPunitionDialog(eleve)) { int nbPunition = Integer.parseInt(nbPunitionLabel.getText()); nbPunitionLabel.setText("" + (nbPunition + 1)); } }); punitionsBox.getChildren().add(punitionBtn); punitionsBox.getChildren().add(nbPunitions); sanctionsGrid.add(punitionsBox, 3, rowIndex, HPos.LEFT); CheckBox motCarnet = new CheckBox(); Label cumulMot = new Label(); motCarnet.setSelected(model.getMotForSeance(eleve, seanceSelect.getValue()) != null); motCarnet.selectedProperty().addListener((ob, o, checked) -> { Mot mot = model.getMotForSeance(eleve, seanceSelect.getValue()); if (checked && mot == null) { model.createMot(eleve, seanceSelect.getValue()); } else if (mot != null) { model.delete(mot); } writeAndMarkInRed(cumulMot, stats.getMotsUntil(eleve, currentDate.getValue()).size(), 3); }); sanctionsGrid.add(motCarnet, 4, rowIndex, null); writeAndMarkInRed(cumulMot, stats.getMotsUntil(eleve, currentDate.getValue()).size(), 3); sanctionsGrid.add(cumulMot, 5, rowIndex, null); CheckBox exclus = new CheckBox(); TextField motif = new TextField(); Bindings.bindBidirectional(exclus.selectedProperty(), eleveData.exclusProperty()); sanctionsGrid.add(exclus, 6, rowIndex, null); exclus.selectedProperty().addListener((o, oldV, newV) -> { if (!newV && StringUtils.isNotBlank(motif.getText()) && ModalUtil.confirm("Effacer le motif ?", "Effacer le motif ?")) { motif.clear(); } }); Bindings.bindBidirectional(motif.textProperty(), eleveData.MotifProperty()); motif.textProperty().addListener((o, oldV, newV) -> { if (StringUtils.isNotBlank(newV)) { exclus.setSelected(true); } }); GridPane.setMargin(motif, new Insets(0, 10, 0, 0)); sanctionsGrid.add(motif, 7, rowIndex, HPos.LEFT); } private void drawRow(Eleve eleve, int rowIndex) { LogUtil.logStart(); donneesHandler.getOrCreateDonneeForEleve(seanceSelect.getValue(), eleve); drawVieScolaire(eleve, rowIndex); drawTravail(eleve, rowIndex); drawSanctions(eleve, rowIndex); LogUtil.logEnd(); } private void writeAndMarkInRed(Label label, int value, int threshold) { label.setText("" + value); if (value >= threshold) { CssUtil.addClass(label, "text-red"); } else { CssUtil.removeClass(label, "text-red"); } } @Override public void reload() { super.reload(); refreshGrid(); } }