/* * 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 mytime.gui.controller; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXDialog; import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.controls.JFXMasonryPane; import com.jfoenix.controls.JFXSpinner; import com.jfoenix.controls.JFXTextField; import com.jfoenix.validation.RequiredFieldValidator; import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.Icon; import java.io.IOException; import java.net.URL; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.concurrent.Service; import javafx.concurrent.Task; import javafx.event.ActionEvent; 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.control.Button; import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.SplitPane; import javafx.scene.effect.GaussianBlur; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.ClipboardContent; import javafx.scene.input.DataFormat; import javafx.scene.input.Dragboard; import javafx.scene.input.TransferMode; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.text.Font; import javafx.scene.text.TextAlignment; import mytime.be.Group; import mytime.be.Location; import mytime.be.Person; import mytime.bll.SortingStrategyFirstName; import mytime.bll.SortingStrategyGuildsAlphabetic; import mytime.bll.Sortings; import mytime.gui.model.ManagerModel; import org.controlsfx.validation.ValidationMessage; import org.controlsfx.validation.decoration.GraphicValidationDecoration; import org.controlsfx.validation.decoration.ValidationDecoration; import mytime.gui.model.Model; /** * FXML Controller class * * @author Stefan Olsen */ public class ManagerGuildMainViewController implements Initializable { @FXML private Label lblGuildName; @FXML private FlowPane masonryPaneGuilds; @FXML private JFXTextField txtFieldSearchAllVolunteers; @FXML private JFXTextField txtFieldSearchVolunteersInGuild; @FXML private JFXTextField txtFieldSearchGuilds; @FXML private FlowPane masonryPaneAllVolunteers; private ManagerModel mgrModel; private ExecutorService execService; private Executor exec; @FXML private FlowPane tilePaneVolunteersInGuild; @FXML private SplitPane splitPane; @FXML private ScrollPane scrollPane1; private Timeline animation; @FXML private ScrollPane scrollPaneAllVolunteers; private final DataFormat buttonFormat = new DataFormat("com.example.myapp.formats.button"); private Button draggingButton; private JFXDialog dialogConfirmation, dialogCreateNewGuild; @FXML private StackPane stackPane; private Label lblDialogMessage; private Timer timer; private TimerTask timerTask; private int q; private List<Node> allPersonsNodesUnSorted; @FXML private HBox headerPane; private boolean firstTimeLoadingNodes; @FXML private ScrollPane scrollPaneGroups; /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { initDialogLbl(); initCreateNewGuildDialog(); initHeaderPane(); timer = new Timer(true); splitPane.getDividers().stream().forEach((d) -> { d.setPosition(0.60); }); mgrModel = ManagerModel.getInstance(); setupExecutorService(); loadAllGroups(); addDropHandling(); tilePaneVolunteersInGuild.prefWidthProperty().bind(scrollPane1.widthProperty()); masonryPaneAllVolunteers.prefWidthProperty().bind(scrollPaneAllVolunteers.widthProperty()); masonryPaneGuilds.setHgap(15); firstTimeLoadingNodes = true; txtFieldSearchAllVolunteers.textProperty().bindBidirectional(mgrModel.getQueryAllPersons()); mgrModel.getQueryAllPersons().addListener(new ChangeListener() { @Override public void changed(ObservableValue ov, Object t, Object t1) { //Filter all volunteers masonrypane if (mgrModel.getAllPersonsGuildTab() != null) { List<Node> filteredList = Model.getInstance().filterAnyListOfNodes((String) t1, mgrModel.getAllPersonsGuildTab()); masonryPaneAllVolunteers.getChildren().setAll(mgrModel.applySortingStrategy(new Sortings(new SortingStrategyFirstName()), filteredList)); } } }); txtFieldSearchVolunteersInGuild.textProperty().bindBidirectional(mgrModel.getQueryPersonsInGuild()); mgrModel.getQueryPersonsInGuild().addListener(new ChangeListener() { @Override public void changed(ObservableValue ov, Object t, Object t1) { if (mgrModel.getAllPersonsInGuild() != null) { List<Node> filteredList = Model.getInstance().filterAnyListOfNodes((String) t1, mgrModel.getAllPersonsInGuild()); tilePaneVolunteersInGuild.getChildren().setAll(mgrModel.applySortingStrategy(new Sortings(new SortingStrategyFirstName()), filteredList)); } } }); } /** * Creats a dialog to show the user an action confirmation. */ private void initDialogLbl() { lblDialogMessage = new Label(); lblDialogMessage.getStyleClass().add("lblDialogMessage"); ImageView lblImg = new ImageView(new Image("/mytime/gui/view/css/checked.png")); lblImg.setFitWidth(24); lblImg.setFitHeight(24); lblImg.setPreserveRatio(true); lblImg.setSmooth(true); lblImg.setCache(true); lblDialogMessage.setGraphic(lblImg); JFXDialogLayout layout = new JFXDialogLayout(); layout.getChildren().add(lblDialogMessage); dialogConfirmation = new JFXDialog(stackPane, layout, JFXDialog.DialogTransition.CENTER); URL url2 = this.getClass().getResource("/mytime/gui/view/css/dialogStackPane.css"); String css2 = url2.toExternalForm(); dialogConfirmation.getStylesheets().add(css2); dialogConfirmation.setOnDialogClosed(e -> { timer.cancel(); }); dialogConfirmation.setOnDialogOpened(e -> { timer = new Timer(true); timerTask = new TimerTask() { @Override public void run() { dialogConfirmation.close(); } }; timer.schedule(timerTask, 700); }); } /** * Create new guild dialog. */ private void initCreateNewGuildDialog() { JFXDialogLayout layout = new JFXDialogLayout(); Label lblHeader = new Label("Opret et nyt laug", new ImageView(new Image("/mytime/gui/view/css/Add.png"))); lblHeader.setFont(Font.font(20)); GridPane gridPane = new GridPane(); gridPane.setPadding(new Insets(5)); gridPane.setHgap(5); gridPane.setVgap(25); ColumnConstraints column1 = new ColumnConstraints(100); ColumnConstraints column2 = new ColumnConstraints(50, 150, 300); column2.setHgrow(Priority.ALWAYS); gridPane.getColumnConstraints().addAll(column1, column2); Label fNameLbl = new Label("Navn:"); JFXTextField guildName = new JFXTextField(); guildName.focusedProperty().addListener((o, oldVal, newVal) -> { if (!newVal) { paintInputs(guildName); } }); RequiredFieldValidator rfv = new RequiredFieldValidator(); rfv.setMessage("Navn kan ikke være tomt"); rfv.setIcon(new Icon(AwesomeIcon.WARNING, "1em", ";", "error")); guildName.getValidators().add(rfv); Label lNameLbl = new Label("Vælg afdeling:"); ValidationDecoration iconDecorator = new GraphicValidationDecoration(); JFXComboBox<Location> guildLocation = new JFXComboBox(); guildLocation.setPromptText("Vælg en lokation hvor det laug befinder sig"); try { guildLocation.getItems().addAll(Model.getInstance().getAllLocations()); } catch (SQLException ex) { ex.printStackTrace(); } guildLocation.focusedProperty().addListener((o, oldVal, newVal) -> { iconDecorator.removeDecorations(guildLocation); }); JFXButton saveButt = new JFXButton("Gem"); saveButt.setOnAction(e -> { boolean b = paintInputs(guildName); if (guildLocation.getSelectionModel().getSelectedItem() == null || !b) { iconDecorator.removeDecorations(guildLocation); iconDecorator.applyValidationDecoration(ValidationMessage.error(guildLocation, "")); } else { try { Group newGroup = mgrModel.createNewGuild(guildName.getText().trim(), guildLocation.getSelectionModel().getSelectedItem()); dialogCreateNewGuild.close(); Platform.runLater(() -> { try { masonryPaneGuilds.getChildren().add(getOneGuild(newGroup)); } catch (IOException ex) { ex.printStackTrace(); } }); } catch (SQLException ex) { ex.printStackTrace(); } } }); GridPane.setHalignment(fNameLbl, HPos.RIGHT); gridPane.add(fNameLbl, 0, 0); GridPane.setHalignment(lNameLbl, HPos.RIGHT); gridPane.add(lNameLbl, 0, 1); GridPane.setHalignment(guildName, HPos.LEFT); gridPane.add(guildName, 1, 0); GridPane.setHalignment(guildLocation, HPos.LEFT); gridPane.add(guildLocation, 1, 1); GridPane.setHalignment(saveButt, HPos.RIGHT); gridPane.add(saveButt, 1, 2); layout.setHeading(lblHeader); layout.setBody(gridPane); dialogCreateNewGuild = new JFXDialog(stackPane, layout, JFXDialog.DialogTransition.LEFT); dialogCreateNewGuild.setOnDialogClosed(e -> { iconDecorator.removeDecorations(guildLocation); guildName.resetValidation(); guildName.clear(); guildLocation.getSelectionModel().clearSelection(); guildName.setUnFocusColor(Paint.valueOf("BLACK")); }); } private boolean paintInputs(JFXTextField node) { boolean b = node.validate(); if (b) { node.setUnFocusColor(Paint.valueOf("BLACK")); } else { node.setUnFocusColor(Paint.valueOf("RED")); } return b; } /** * Multithreading setup. */ private void setupExecutorService() { execService = Executors.newCachedThreadPool((Runnable r) -> { Thread t = Executors.defaultThreadFactory().newThread(r); t.setDaemon(true); return t; }); exec = Executors.newCachedThreadPool(runnable -> { Thread t = new Thread(runnable); t.setDaemon(true); return t; }); } /** * * @param groupToDisplay * @return A node as JFXButton * @throws IOException */ private Node getOneGuild(Group groupToDisplay) throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/mytime/gui/view/ManagerGuildOneGuildForListView.fxml")); Node node = loader.load(); JFXButton btn = (JFXButton) node; btn.setPrefWidth(masonryPaneGuilds.prefWidthProperty().get()); btn.setPrefHeight(15); btn.setText(groupToDisplay.getName().get()); btn.setUserData(groupToDisplay); btn.setTextAlignment(TextAlignment.LEFT); setbtnOneGuildOnAction(btn); return node; } /** * Loads the People in the selected group in the flowpane. * * @param btn */ private void setbtnOneGuildOnAction(JFXButton btn) { Service<List<Person>> service = new Service<List<Person>>() { @Override protected Task<List<Person>> createTask() { Task<List<Person>> personsInGuild = new Task<List<Person>>() { @Override protected List<Person> call() throws Exception { try { return mgrModel.getPersonsInGroup((Group) btn.getUserData()); } catch (SQLException ex) { return null; } } }; personsInGuild.setOnSucceeded(e -> { List<Person> personsInGroup = personsInGuild.getValue(); Task<List<Node>> nodes = new Task() { List<Node> personsInGroupNodes = Collections.synchronizedList(new ArrayList<>()); @Override protected Object call() throws Exception { personsInGroup.stream().parallel().forEach(l -> { try { Node node = getNodeForVolunteer(l, true); personsInGroupNodes.add(node); } catch (IOException ex) { ex.printStackTrace(); } }); mgrModel.getAllPersonsInGuild().clear(); mgrModel.getAllPersonsInGuild().addAll(personsInGroupNodes);//Added later return personsInGroupNodes; } }; nodes.setOnSucceeded(o -> { List<Node> nodesToSet = nodes.getValue(); if (nodesToSet != null) { execService.execute(getAvailableVolunteerNodesForGroup((Group) btn.getUserData())); Platform.runLater(() -> { tilePaneVolunteersInGuild.getChildren().setAll(mgrModel.applySortingStrategy(new Sortings(new SortingStrategyFirstName()), nodesToSet)); // masonryPaneAllVolunteers.getChildren().setAll(mgrModel.applySortingStrategy(new Sortings(new SortingStrategyFirstName()), getAvailableVolunteerNodesForGroup((Group) btn.getUserData()))); tilePaneVolunteersInGuild.requestLayout(); masonryPaneAllVolunteers.requestLayout(); }); } }); execService.execute(nodes); }); return personsInGuild; } }; btn.setOnAction(e -> { if (firstTimeLoadingNodes) { runDisableTimer(700); firstTimeLoadingNodes = false; } else { runDisableTimer(300); } masonryPaneGuilds.getChildren().stream().parallel().forEach(b -> { JFXButton jBtn = (JFXButton) b; jBtn.setStyle(null); }); btn.setStyle("-fx-border-color: #6666ff;-fx-border-width: 0 0 0 3;"); tilePaneVolunteersInGuild.getChildren().setAll(new JFXSpinner()); Group group = (Group) btn.getUserData(); mgrModel.setCurrentlyChosenGroup(group); Platform.runLater(() -> { masonryPaneAllVolunteers.getChildren().setAll(new JFXSpinner()); if (!service.isRunning()) { service.reset(); service.start(); lblGuildName.setText(group.getName().get()); } }); }); } /** * Loads all the groups from the model. */ private void loadAllGroups() { Task<List<Group>> getGroupsTask = new Task<List<Group>>() { List<Group> allGroups = null; @Override protected List<Group> call() throws Exception { return allGroups = mgrModel.getAllGroups(); } }; getGroupsTask.setOnSucceeded(e -> { List<Group> allGroups = getGroupsTask.getValue(); List<Node> allGroupNodes = Collections.synchronizedList(new ArrayList<>()); if (allGroups != null) { allGroups.stream().parallel().forEach(a -> { try { allGroupNodes.add(getOneGuild(a)); } catch (IOException ex) { ex.printStackTrace(); } }); Platform.runLater(() -> { masonryPaneGuilds.getChildren().setAll(mgrModel.applySortingStrategy(new Sortings(new SortingStrategyGuildsAlphabetic()), allGroupNodes)); masonryPaneGuilds.requestLayout(); }); scrollPaneGroups.requestFocus(); } }); execService.execute(getGroupsTask); } /** * Method for getting a volunteer as a node, so that we can load it in our * view as a JAVAFX component. * * @param volunteer * @return * @throws IOException */ private Node getNodeForVolunteer(Person volunteer, boolean isInGuild) throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/mytime/gui/view/ManagerGuildOneVolunteer.fxml")); Node node = loader.load(); Button button = (Button) node; // load the image Image image = new Image(volunteer.getProfilePicture().get()); // simple displays ImageView the image as is ImageView iv1 = new ImageView(); // iv1.setFitWidth(70); iv1.setFitHeight(50); Circle clip = new Circle(iv1.getFitHeight() / 2, iv1.getFitWidth() / 3, 26); iv1.setClip(clip); iv1.setImage(image); iv1.setPreserveRatio(true); iv1.setSmooth(true); iv1.setCache(true); button.setPrefHeight(70); button.setPrefWidth(150); if (isInGuild) { button.getStyleClass().add("VolunteerBtnInGuild"); hoverBlur(button, true); deleteAction(button, true); } else { button.getStyleClass().add("VolunteerBtn"); } String firstName = volunteer.getFirstName().get(); String lastName = volunteer.getLastName().get(); // button.setText(firstName + "\n" + lastName); button.setText(""); StackPane grap = new StackPane(); HBox vbox = new HBox(); vbox.setAlignment(Pos.CENTER_LEFT); vbox.setSpacing(4); Label lblName = new Label(); lblName.setText(firstName + "\n" + lastName); vbox.getChildren().addAll(iv1, lblName); grap.setAlignment(Pos.CENTER_LEFT); grap.getChildren().add(vbox); button.setGraphic(grap); // button.setScaleX(0); // button.setScaleY(0); button.setContentDisplay(ContentDisplay.LEFT); button.setUserData(volunteer); // animation = new Timeline(new KeyFrame(Duration.millis(240), new KeyValue(button.scaleXProperty(), 1, Interpolator.EASE_BOTH), // new KeyValue(button.scaleYProperty(), 1, Interpolator.EASE_BOTH))); // animation.setDelay(Duration.millis(100 * 2 + (100))); // animation.play(); return node; } /** * Blurs the button when you hover over it. * * @param b * @param active */ private void hoverBlur(Button b, boolean active) { if (active) { b.setOnMouseEntered(e -> { StackPane pane = (StackPane) b.getGraphic(); HBox hbox = (HBox) pane.getChildren().get(0); hbox.setEffect(new GaussianBlur()); }); b.setOnMouseExited(e -> { StackPane pane = (StackPane) b.getGraphic(); HBox hbox = (HBox) pane.getChildren().get(0); hbox.setEffect(null); }); } else { b.setOnMouseEntered(null); b.setOnMouseExited(null); } } /** * Filters a list of nodes * * @param group to filter by. Those persons that are in this group are not * in the return list of nodes. * @return A list of volunteer nodes available for adding to a new group */ private Task<List<Node>> getAvailableVolunteerNodesForGroup(Group group) { Task<List<Node>> availableVolunteerNodes = new Task<List<Node>>() { @Override protected List<Node> call() throws Exception { if (allPersonsNodesUnSorted == null) { List<Node> returnNodes = Collections.synchronizedList(FXCollections.observableArrayList()); List<Node> allPersonNodes = mgrModel.getAllPersonNodes(); allPersonNodes.stream().parallel().forEach(e -> { Person p = (Person) e.getUserData(); try { returnNodes.add(getNodeForVolunteer(p, false)); } catch (IOException ex) { ex.printStackTrace(); } allPersonsNodesUnSorted = returnNodes; }); } List<Node> returnNodes = allPersonsNodesUnSorted.stream().collect(Collectors.toList()); try { List<Person> personsInCurrentGroup = Collections.synchronizedList(new ArrayList<>()); personsInCurrentGroup.addAll(mgrModel.getPersonsInGroup(group)); Iterator<Node> iter = returnNodes.iterator(); while (iter.hasNext()) { Node clg = iter.next(); Person p = (Person) clg.getUserData(); personsInCurrentGroup.stream().forEach(e -> { if (e.getId().get() == p.getId().get()) { iter.remove(); } }); } returnNodes.stream().parallel().forEach(e -> { Button b = (Button) e; initDragDrop(b, true); hoverBlur(b, false); deleteAction(b, false); b.getStyleClass().clear(); b.getStyleClass().add("VolunteerBtn"); }); } catch (SQLException ex) { ex.printStackTrace(); } mgrModel.getAllPersonsGuildTab().clear(); mgrModel.getAllPersonsGuildTab().addAll(returnNodes);//Added later return returnNodes; } }; availableVolunteerNodes.setOnSucceeded(e -> { masonryPaneAllVolunteers.getChildren().setAll(mgrModel.applySortingStrategy(new Sortings(new SortingStrategyFirstName()), availableVolunteerNodes.getValue())); }); return availableVolunteerNodes; } /** * Sets up the dragable * * @param b Button to be dragable. */ private void initDragDrop(Button b, boolean active) { if (active) { b.setOnDragDetected(e -> { Dragboard db = b.startDragAndDrop(TransferMode.MOVE); db.setDragView(b.snapshot(null, null)); ClipboardContent cc = new ClipboardContent(); cc.put(buttonFormat, "button"); db.setContent(cc); draggingButton = b; }); b.setOnDragDone(e -> draggingButton = null); } else { b.setOnDragDetected(null); } } /** * Part of the drag'n'drop function. Sets the actions on the panes that * should accept the dragables. */ private void addDropHandling() { scrollPane1.setOnDragOver(e -> { Dragboard db = e.getDragboard(); if (db.hasContent(buttonFormat) && draggingButton != null) { e.acceptTransferModes(TransferMode.MOVE); } }); scrollPane1.setOnDragDropped(e -> { Dragboard db = e.getDragboard(); if (db.hasContent(buttonFormat)) { Person person = (Person) draggingButton.getUserData(); masonryPaneAllVolunteers.getChildren().remove(draggingButton); tilePaneVolunteersInGuild.getChildren().add(draggingButton); mgrModel.getAllPersonsGuildTab().remove(draggingButton); mgrModel.getAllPersonsInGuild().add(draggingButton);//For searching Platform.runLater(() -> { try { Model.getInstance().addPersonToGroup(person, mgrModel.getCurrentlyChosenGuild()); tilePaneVolunteersInGuild.requestLayout(); masonryPaneAllVolunteers.requestLayout(); showDialog("Tilføjet!", 1000); } catch (SQLException ex) { ex.printStackTrace(); } }); e.setDropCompleted(true); draggingButton = null; tilePaneVolunteersInGuild.getChildren().stream().forEach(node -> { Person p = (Person) node.getUserData(); if (p.getId().get() == person.getId().get()) { Button b = (Button) node; hoverBlur(b, true); deleteAction(b, true); initDragDrop(b, false); b.getStyleClass().add("VolunteerBtnInGuild"); } }); } }); } /** * Display a confirmed message * * @param message String to display. */ private void showDialog(String message, long millisecs) { lblDialogMessage.setText(message); dialogConfirmation.show(stackPane); } /** * Set on action when you press on a volunteer in a guild. * * @param button * @param active */ private void deleteAction(Button button, boolean active) { if (active) { button.setOnAction(e -> { try { Person personToDelete = (Person) button.getUserData(); Model.getInstance().removePersonFromGroup(personToDelete, mgrModel.getCurrentlyChosenGuild()); tilePaneVolunteersInGuild.getChildren().remove(button); hoverBlur(button, false); initDragDrop(button, active); masonryPaneAllVolunteers.getChildren().add(button); button.setOnAction(null); button.getStyleClass().clear(); button.getStyleClass().add("VolunteerBtn"); mgrModel.getAllPersonsGuildTab().add(button); mgrModel.getAllPersonsInGuild().remove(button);//for searching showDialog("Fjernet fra dette laug", 1000); } catch (SQLException ex) { ex.printStackTrace(); } }); } else { button.setOnAction(null); } } /** * Opens the dialog when you press create new guild. * * @param event */ @FXML private void handleCreateNewGuild(ActionEvent event) { dialogCreateNewGuild.show(); } /** * headerpane label and background */ private void initHeaderPane() { try { FXMLLoader lblLoader = new FXMLLoader(getClass().getResource("/mytime/gui/view/header.fxml")); Node lblNode = lblLoader.load(); Label lblHeader = (Label) lblNode; // lblHeader.setFont(Font.font(45)); // lblHeader.setTextFill(Color.rgb(252, 252, 252, .99)); headerPane.getChildren().add(lblHeader); headerPane.getStyleClass().add("headerPane"); headerPane.setAlignment(Pos.BOTTOM_LEFT); StackPane.setMargin(lblHeader, new Insets(0, 0, 0, 80)); StackPane.setAlignment(lblHeader, Pos.CENTER_LEFT); } catch (IOException ex) { ex.printStackTrace(); } } /** * Prevents autistic behaviour */ private void runDisableTimer(long delay) { Timer timer = new Timer(true); masonryPaneGuilds.setDisable(true); TimerTask task = new TimerTask() { @Override public void run() { masonryPaneGuilds.setDisable(false); } }; timer.schedule(task, delay); } }