Repository for the HealthTool which enables Apple users to analyse their health data from the Apple health app and prepares the data for contributing it for future studies on wearable data.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

426 lines
10 KiB

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<Boolean>
{
/**
* 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<MouseEvent>()
{
@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<MouseEvent>()
{
@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<String, Integer> 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<MouseEvent>()
{
@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<Boolean> 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<? extends Boolean> 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<String> getSelectedSubElementNames()
{
List<String> 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<SubEntryView> getSelectedSubElements()
{
List<SubEntryView> 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);
}
}
}
}
}