package application.customviews; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import application.UICoordinator; import application.enums.EntryType; import application.helpers.wrappers.Element; import application.helpers.wrappers.SubElement; import application.res.Backgrounds; import application.res.Text; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; /** * This class represents one element in the select view where the user can * select the data he / she wishes to contribute. The elements can contain * {@link SubEntryView}s with sub elements. Every EntryView can contain an info * view and may be opened for the sub elements (if they exist). * * @author Bianca * */ public class EntryView extends VBox implements ChangeListener { /** * The {@link Element} represented by this {@link EntryView}. */ private Element elem; /** * The header of the element. Is always visible. */ private AnchorPane header; /** * Contains the detailed information for the element as defined in its * {@link EntryType}. */ private BorderPane details; /** * Contains the sub elements if they exist. If there are no subElements, the * view will never be visible and there won't be a button to show it. */ private BorderPane subItem; /** * Indicates whether the information part is currently shown. */ private boolean infoOpen = false; /** * Indicates whether the sub elements are currently shown. */ private boolean subOpen = false; /** * Indicates whether the whole element with all its sub elements is selected. */ private BooleanProperty selected; /** * Represents the button to open the information view of the element. */ private BorderPane help; /** * Represents the button to show the sub elements. */ private BorderPane open; /** * Number of all sub elements. */ private int subElemCount = 0; /** * Number of all selected sub elements. */ private int selectedSubElemCount = 0; /** * Sets up an {@link EntryView} without sub elements. * * @param elem The data of this entry. */ public EntryView(Element elem) { super(); this.elem = elem; selected = new SimpleBooleanProperty(false); header = new AnchorPane(); details = new BorderPane(); details.setId("Details"); subItem = new BorderPane(); subItem.setId("SubItem"); details.setCenter(new Label(elem.getType().getDesc())); header.minWidthProperty().bind(Bindings.createDoubleBinding(() -> this.getWidth(), this.widthProperty())); header.maxWidthProperty().bind(Bindings.createDoubleBinding(() -> this.getWidth(), this.widthProperty())); // right side (help button for showing further information) help = new BorderPane(); help.setId("help"); ImageView helpImage = new ImageView(new Image(UICoordinator.class.getResourceAsStream("help.png"))); help.setCenter(helpImage); AnchorPane.setRightAnchor(help, 0.0); AnchorPane.setTopAnchor(help, 0.0); AnchorPane.setBottomAnchor(help, 0.0); help.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent arg0) { // always close sub elements first if (subOpen) { closeSub(); } if (infoOpen) { closeInfo(); } else { getChildren().add(1, details); infoOpen = true; help.setBackground(Backgrounds.greyBackground); } } }); // middle (name and value etc) Label text = new Label(String.format(Text.F_ME_SAVE, elem.getType().getValue(), elem.getValue())); AnchorPane.setLeftAnchor(text, 50.0); AnchorPane.setTopAnchor(text, 0.0); AnchorPane.setBottomAnchor(text, 0.0); AnchorPane.setRightAnchor(text, 50.0); header.getChildren().add(text); text.minWidthProperty().bind( Bindings.createDoubleBinding(() -> header.getWidth() - help.getWidth() * 2, header.widthProperty())); text.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent event) { setSelected(!getSelected(), true); } }); header.getChildren().add(help); getChildren().add(header); } /** * Sets up an {@link EntryView} with sub elements. * * @param elem The data of this entry. * @param subEntries The sub elements which belong to this element */ public EntryView(Element elem, HashMap subEntries) { this(elem); VBox subContainer = new VBox(); for (String entry : subEntries.keySet()) { subElemCount++; SubEntryView sub = new SubEntryView(new SubElement(entry, subEntries.get(entry))); sub.addCheckListener(this); subContainer.getChildren().add(sub); } subItem.setCenter(subContainer); // left side open = new BorderPane(); ImageView openImage = new ImageView(new Image(UICoordinator.class.getResourceAsStream("dreieck2.png"))); open.setId("open"); AnchorPane.setLeftAnchor(open, 0.0); AnchorPane.setTopAnchor(open, 0.0); AnchorPane.setBottomAnchor(open, 0.0); open.setCenter(openImage); openImage.setRotate(180); open.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent arg0) { // always close info first if (infoOpen) { closeInfo(); } if (subOpen) { closeSub(); } else { openSub(); } } }); header.getChildren().add(open); } /** * Returns the value / count of this entry. * * @return The described value. */ public String getValue() { return elem.getValue(); } /** * Closes the detailed information view if it is open */ private void closeInfo() { getChildren().remove(details); infoOpen = false; help.setBackground(Backgrounds.darkGreyBackground); } /** * Shows the sub elements if they are not open yet. */ private void openSub() { getChildren().add(subItem); subOpen = true; open.setBackground(Backgrounds.greyBackground); } /** * Closes the sub elements view if it is not closed yet. */ private void closeSub() { getChildren().remove(subItem); subOpen = false; open.setBackground(Backgrounds.darkGreyBackground); } /** * Sets the value of the selection. If wanted it can also adjust the states of * the {@link SubEntryView}s * * @param value New value of the selection. * @param all If true then the {@link SubEntryView}s will also be adjusted. * Otherwise they won't be changed. */ public void setSelected(boolean value, boolean all) { this.selected.set(value); // adjust color of the entry if (!value) { header.setBackground(null); } else { header.setBackground(Backgrounds.greenBackground); } // adjust the sub elements if (all && subItem.getCenter() != null) { // there are subitems for (Node child : ((VBox) subItem.getCenter()).getChildren()) { SubEntryView sub = (SubEntryView) child; sub.setSelected(value); } } } /** * Gets the selection. * * @return {@code True} if the entry or any sub entry is selected. {@code False} * otherwise. */ public boolean getSelected() { return selectedSubElemCount > 0 || selected.getValue(); } /** * Allows for a listener to be added to the selection value. * * @param listen Listener to be added. */ public void addCheckListener(ChangeListener listen) { selected.addListener(listen); } /** * Returns the {@link EntryType} of this {@link EntryView}. * * @return The described value. */ public EntryType getEntryType() { return this.elem.getType(); } @Override public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { // Here the sub elements are watched and the selected ones counted if (newValue) { selectedSubElemCount++; } else { selectedSubElemCount--; } // if any sub element was selected then of course the whole // element also needs to be selected. if (selectedSubElemCount > 0) { this.selected.set(true); } else { this.selected.set(false); } // this adjusts the color of the element but not the selection of the sub // elements if (selectedSubElemCount == subElemCount) { setSelected(true, false); } else if (selectedSubElemCount > 0) { header.setBackground(Backgrounds.lightGreenBackground); } else { setSelected(false, false); } } /** * Returns a list of readable names of all sub elements or an empty list if * their are no sub elements. * * @return The described value. */ public List getSelectedSubElementNames() { List res = new ArrayList<>(); if (subItem.getCenter() != null) { // there are subitems for (Node child : ((VBox) subItem.getCenter()).getChildren()) { SubEntryView sub = (SubEntryView) child; if (sub.isSelected()) { res.add(sub.getName()); } } } return res; } /** * Returns all {@link SubEntryView}s of this entry in a list or an empty lift if * there are now sub elements. * * @return The described value. */ public List getSelectedSubElements() { List res = new ArrayList<>(); if (subItem.getCenter() != null) { // there are sub elements for (Node child : ((VBox) subItem.getCenter()).getChildren()) { SubEntryView sub = (SubEntryView) child; if (sub.isSelected()) { res.add(sub); } } } return res; } public void selectSubelement(String elementName) { if (subItem.getCenter() != null) { // there are subitems for (Node child : ((VBox) subItem.getCenter()).getChildren()) { SubEntryView sub = (SubEntryView) child; if (sub.getName().contains(elementName)) { sub.setSelected(true); } } } } }