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

3 years ago
  1. package application.customviews;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import application.UICoordinator;
  6. import application.enums.EntryType;
  7. import application.helpers.wrappers.Element;
  8. import application.helpers.wrappers.SubElement;
  9. import application.res.Backgrounds;
  10. import application.res.Text;
  11. import javafx.beans.binding.Bindings;
  12. import javafx.beans.property.BooleanProperty;
  13. import javafx.beans.property.SimpleBooleanProperty;
  14. import javafx.beans.value.ChangeListener;
  15. import javafx.beans.value.ObservableValue;
  16. import javafx.event.EventHandler;
  17. import javafx.scene.Node;
  18. import javafx.scene.control.Label;
  19. import javafx.scene.image.Image;
  20. import javafx.scene.image.ImageView;
  21. import javafx.scene.input.MouseEvent;
  22. import javafx.scene.layout.AnchorPane;
  23. import javafx.scene.layout.BorderPane;
  24. import javafx.scene.layout.VBox;
  25. /**
  26. * This class represents one element in the select view where the user can
  27. * select the data he / she wishes to contribute. The elements can contain
  28. * {@link SubEntryView}s with sub elements. Every EntryView can contain an info
  29. * view and may be opened for the sub elements (if they exist).
  30. *
  31. * @author Bianca
  32. *
  33. */
  34. public class EntryView extends VBox implements ChangeListener<Boolean>
  35. {
  36. /**
  37. * The {@link Element} represented by this {@link EntryView}.
  38. */
  39. private Element elem;
  40. /**
  41. * The header of the element. Is always visible.
  42. */
  43. private AnchorPane header;
  44. /**
  45. * Contains the detailed information for the element as defined in its
  46. * {@link EntryType}.
  47. */
  48. private BorderPane details;
  49. /**
  50. * Contains the sub elements if they exist. If there are no subElements, the
  51. * view will never be visible and there won't be a button to show it.
  52. */
  53. private BorderPane subItem;
  54. /**
  55. * Indicates whether the information part is currently shown.
  56. */
  57. private boolean infoOpen = false;
  58. /**
  59. * Indicates whether the sub elements are currently shown.
  60. */
  61. private boolean subOpen = false;
  62. /**
  63. * Indicates whether the whole element with all its sub elements is selected.
  64. */
  65. private BooleanProperty selected;
  66. /**
  67. * Represents the button to open the information view of the element.
  68. */
  69. private BorderPane help;
  70. /**
  71. * Represents the button to show the sub elements.
  72. */
  73. private BorderPane open;
  74. /**
  75. * Number of all sub elements.
  76. */
  77. private int subElemCount = 0;
  78. /**
  79. * Number of all selected sub elements.
  80. */
  81. private int selectedSubElemCount = 0;
  82. /**
  83. * Sets up an {@link EntryView} without sub elements.
  84. *
  85. * @param elem The data of this entry.
  86. */
  87. public EntryView(Element elem)
  88. {
  89. super();
  90. this.elem = elem;
  91. selected = new SimpleBooleanProperty(false);
  92. header = new AnchorPane();
  93. details = new BorderPane();
  94. details.setId("Details");
  95. subItem = new BorderPane();
  96. subItem.setId("SubItem");
  97. details.setCenter(new Label(elem.getType().getDesc()));
  98. header.minWidthProperty().bind(Bindings.createDoubleBinding(() -> this.getWidth(), this.widthProperty()));
  99. header.maxWidthProperty().bind(Bindings.createDoubleBinding(() -> this.getWidth(), this.widthProperty()));
  100. // right side (help button for showing further information)
  101. help = new BorderPane();
  102. help.setId("help");
  103. ImageView helpImage = new ImageView(new Image(UICoordinator.class.getResourceAsStream("help.png")));
  104. help.setCenter(helpImage);
  105. AnchorPane.setRightAnchor(help, 0.0);
  106. AnchorPane.setTopAnchor(help, 0.0);
  107. AnchorPane.setBottomAnchor(help, 0.0);
  108. help.setOnMouseClicked(new EventHandler<MouseEvent>()
  109. {
  110. @Override
  111. public void handle(MouseEvent arg0)
  112. {
  113. // always close sub elements first
  114. if (subOpen)
  115. {
  116. closeSub();
  117. }
  118. if (infoOpen)
  119. {
  120. closeInfo();
  121. }
  122. else
  123. {
  124. getChildren().add(1, details);
  125. infoOpen = true;
  126. help.setBackground(Backgrounds.greyBackground);
  127. }
  128. }
  129. });
  130. // middle (name and value etc)
  131. Label text = new Label(String.format(Text.F_ME_SAVE, elem.getType().getValue(), elem.getValue()));
  132. AnchorPane.setLeftAnchor(text, 50.0);
  133. AnchorPane.setTopAnchor(text, 0.0);
  134. AnchorPane.setBottomAnchor(text, 0.0);
  135. AnchorPane.setRightAnchor(text, 50.0);
  136. header.getChildren().add(text);
  137. text.minWidthProperty().bind(
  138. Bindings.createDoubleBinding(() -> header.getWidth() - help.getWidth() * 2, header.widthProperty()));
  139. text.setOnMouseClicked(new EventHandler<MouseEvent>()
  140. {
  141. @Override
  142. public void handle(MouseEvent event)
  143. {
  144. setSelected(!getSelected(), true);
  145. }
  146. });
  147. header.getChildren().add(help);
  148. getChildren().add(header);
  149. }
  150. /**
  151. * Sets up an {@link EntryView} with sub elements.
  152. *
  153. * @param elem The data of this entry.
  154. * @param subEntries The sub elements which belong to this element
  155. */
  156. public EntryView(Element elem, HashMap<String, Integer> subEntries)
  157. {
  158. this(elem);
  159. VBox subContainer = new VBox();
  160. for (String entry : subEntries.keySet())
  161. {
  162. subElemCount++;
  163. SubEntryView sub = new SubEntryView(new SubElement(entry, subEntries.get(entry)));
  164. sub.addCheckListener(this);
  165. subContainer.getChildren().add(sub);
  166. }
  167. subItem.setCenter(subContainer);
  168. // left side
  169. open = new BorderPane();
  170. ImageView openImage = new ImageView(new Image(UICoordinator.class.getResourceAsStream("dreieck2.png")));
  171. open.setId("open");
  172. AnchorPane.setLeftAnchor(open, 0.0);
  173. AnchorPane.setTopAnchor(open, 0.0);
  174. AnchorPane.setBottomAnchor(open, 0.0);
  175. open.setCenter(openImage);
  176. openImage.setRotate(180);
  177. open.setOnMouseClicked(new EventHandler<MouseEvent>()
  178. {
  179. @Override
  180. public void handle(MouseEvent arg0)
  181. {
  182. // always close info first
  183. if (infoOpen)
  184. {
  185. closeInfo();
  186. }
  187. if (subOpen)
  188. {
  189. closeSub();
  190. }
  191. else
  192. {
  193. openSub();
  194. }
  195. }
  196. });
  197. header.getChildren().add(open);
  198. }
  199. /**
  200. * Returns the value / count of this entry.
  201. *
  202. * @return The described value.
  203. */
  204. public String getValue()
  205. {
  206. return elem.getValue();
  207. }
  208. /**
  209. * Closes the detailed information view if it is open
  210. */
  211. private void closeInfo()
  212. {
  213. getChildren().remove(details);
  214. infoOpen = false;
  215. help.setBackground(Backgrounds.darkGreyBackground);
  216. }
  217. /**
  218. * Shows the sub elements if they are not open yet.
  219. */
  220. private void openSub()
  221. {
  222. getChildren().add(subItem);
  223. subOpen = true;
  224. open.setBackground(Backgrounds.greyBackground);
  225. }
  226. /**
  227. * Closes the sub elements view if it is not closed yet.
  228. */
  229. private void closeSub()
  230. {
  231. getChildren().remove(subItem);
  232. subOpen = false;
  233. open.setBackground(Backgrounds.darkGreyBackground);
  234. }
  235. /**
  236. * Sets the value of the selection. If wanted it can also adjust the states of
  237. * the {@link SubEntryView}s
  238. *
  239. * @param value New value of the selection.
  240. * @param all If true then the {@link SubEntryView}s will also be adjusted.
  241. * Otherwise they won't be changed.
  242. */
  243. public void setSelected(boolean value, boolean all)
  244. {
  245. this.selected.set(value);
  246. // adjust color of the entry
  247. if (!value)
  248. {
  249. header.setBackground(null);
  250. }
  251. else
  252. {
  253. header.setBackground(Backgrounds.greenBackground);
  254. }
  255. // adjust the sub elements
  256. if (all && subItem.getCenter() != null)
  257. {
  258. // there are subitems
  259. for (Node child : ((VBox) subItem.getCenter()).getChildren())
  260. {
  261. SubEntryView sub = (SubEntryView) child;
  262. sub.setSelected(value);
  263. }
  264. }
  265. }
  266. /**
  267. * Gets the selection.
  268. *
  269. * @return {@code True} if the entry or any sub entry is selected. {@code False}
  270. * otherwise.
  271. */
  272. public boolean getSelected()
  273. {
  274. return selectedSubElemCount > 0 || selected.getValue();
  275. }
  276. /**
  277. * Allows for a listener to be added to the selection value.
  278. *
  279. * @param listen Listener to be added.
  280. */
  281. public void addCheckListener(ChangeListener<Boolean> listen)
  282. {
  283. selected.addListener(listen);
  284. }
  285. /**
  286. * Returns the {@link EntryType} of this {@link EntryView}.
  287. *
  288. * @return The described value.
  289. */
  290. public EntryType getEntryType()
  291. {
  292. return this.elem.getType();
  293. }
  294. @Override
  295. public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
  296. {
  297. // Here the sub elements are watched and the selected ones counted
  298. if (newValue)
  299. {
  300. selectedSubElemCount++;
  301. }
  302. else
  303. {
  304. selectedSubElemCount--;
  305. }
  306. // if any sub element was selected then of course the whole
  307. // element also needs to be selected.
  308. if (selectedSubElemCount > 0)
  309. {
  310. this.selected.set(true);
  311. }
  312. else
  313. {
  314. this.selected.set(false);
  315. }
  316. // this adjusts the color of the element but not the selection of the sub
  317. // elements
  318. if (selectedSubElemCount == subElemCount)
  319. {
  320. setSelected(true, false);
  321. }
  322. else if (selectedSubElemCount > 0)
  323. {
  324. header.setBackground(Backgrounds.lightGreenBackground);
  325. }
  326. else
  327. {
  328. setSelected(false, false);
  329. }
  330. }
  331. /**
  332. * Returns a list of readable names of all sub elements or an empty list if
  333. * their are no sub elements.
  334. *
  335. * @return The described value.
  336. */
  337. public List<String> getSelectedSubElementNames()
  338. {
  339. List<String> res = new ArrayList<>();
  340. if (subItem.getCenter() != null)
  341. {
  342. // there are subitems
  343. for (Node child : ((VBox) subItem.getCenter()).getChildren())
  344. {
  345. SubEntryView sub = (SubEntryView) child;
  346. if (sub.isSelected())
  347. {
  348. res.add(sub.getName());
  349. }
  350. }
  351. }
  352. return res;
  353. }
  354. /**
  355. * Returns all {@link SubEntryView}s of this entry in a list or an empty lift if
  356. * there are now sub elements.
  357. *
  358. * @return The described value.
  359. */
  360. public List<SubEntryView> getSelectedSubElements()
  361. {
  362. List<SubEntryView> res = new ArrayList<>();
  363. if (subItem.getCenter() != null)
  364. {
  365. // there are sub elements
  366. for (Node child : ((VBox) subItem.getCenter()).getChildren())
  367. {
  368. SubEntryView sub = (SubEntryView) child;
  369. if (sub.isSelected())
  370. {
  371. res.add(sub);
  372. }
  373. }
  374. }
  375. return res;
  376. }
  377. public void selectSubelement(String elementName)
  378. {
  379. if (subItem.getCenter() != null)
  380. {
  381. // there are subitems
  382. for (Node child : ((VBox) subItem.getCenter()).getChildren())
  383. {
  384. SubEntryView sub = (SubEntryView) child;
  385. if (sub.getName().contains(elementName))
  386. {
  387. sub.setSelected(true);
  388. }
  389. }
  390. }
  391. }
  392. }