InspectorTable.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. package ui.view.inspector;
  2. import java.awt.BorderLayout;
  3. import java.awt.Color;
  4. import java.awt.Container;
  5. import java.awt.Dimension;
  6. import java.math.RoundingMode;
  7. import java.text.NumberFormat;
  8. import java.util.ArrayList;
  9. import java.util.Collection;
  10. import java.util.HashSet;
  11. import java.util.List;
  12. import java.util.Locale;
  13. import java.util.Optional;
  14. import java.util.Set;
  15. import java.util.stream.Collectors;
  16. import java.util.stream.Stream;
  17. import javax.swing.BorderFactory;
  18. import javax.swing.Box;
  19. import javax.swing.BoxLayout;
  20. import javax.swing.ImageIcon;
  21. import javax.swing.JButton;
  22. import javax.swing.JCheckBox;
  23. import javax.swing.JComboBox;
  24. import javax.swing.JFormattedTextField;
  25. import javax.swing.JLabel;
  26. import javax.swing.JPanel;
  27. import javax.swing.JTextField;
  28. import javax.swing.text.NumberFormatter;
  29. import classes.AbstractCanvasObject;
  30. import classes.GroupNode;
  31. import classes.HolonElement;
  32. import classes.HolonElement.Priority;
  33. import classes.HolonObject;
  34. import net.miginfocom.swing.MigLayout;
  35. import ui.controller.Control;
  36. import ui.view.component.TrippleCheckBox;
  37. import ui.view.component.TrippleCheckBox.State;
  38. import utility.ImageImport;
  39. import utility.events.Action;
  40. import utility.events.SimpleDocumentListener;
  41. public class InspectorTable extends JPanel {
  42. private List<ElementRow> elementRows = new ArrayList<ElementRow>();
  43. private Control control;
  44. private final int columnHeight = 20;
  45. //UI
  46. private final TrippleCheckBox selectAllCheckBox = new TrippleCheckBox();
  47. private final JButton addButton = new JButton();
  48. private final JButton duplicateButton = new JButton();
  49. private final JButton deleteButton = new JButton();
  50. private final JPanel buttonPanel = new JPanel();
  51. private final static NumberFormatter doubleFormatter = generateNumberFormatter();
  52. //Colors
  53. private final static Color selectedColor = new Color(126, 186, 255);
  54. private final static Color borderColor = new Color(171, 173, 179);
  55. //Events
  56. public Action<Set<HolonElement>> OnElementSelectionChanged = new Action<>();
  57. public InspectorTable(Control control) {
  58. control.OnSelectionChanged.addListener(() -> update_ui());
  59. this.control = control;
  60. init();
  61. generateHeader();
  62. }
  63. private void init() {
  64. MigLayout layout = new MigLayout("insets 0,gap 0,wrap 7", // Layout Constraints
  65. "[][fill, grow][][fill, grow][fill, grow][][]", // Column constraints
  66. "[25!][20:20:20]"); // Row constraints
  67. this.setLayout(layout);
  68. initSelectAllCheckBox();
  69. initButtonPanel();
  70. }
  71. private static NumberFormatter generateNumberFormatter() {
  72. NumberFormat doubleFormat = NumberFormat.getNumberInstance(Locale.US);
  73. doubleFormat.setMinimumFractionDigits(1);
  74. doubleFormat.setMaximumFractionDigits(10);
  75. doubleFormat.setRoundingMode(RoundingMode.HALF_UP);
  76. doubleFormat.setGroupingUsed(false);
  77. NumberFormatter doubleFormatter = new NumberFormatter(doubleFormat);
  78. doubleFormatter.setCommitsOnValidEdit(true);
  79. doubleFormatter.setValueClass(Double.class);
  80. return doubleFormatter;
  81. }
  82. private void generateHeader() {
  83. this.add(selectAllCheckBox);
  84. this.add(generateHeaderColumnTitle("Object"));
  85. this.add(generateHeaderColumnTitle("Id "));
  86. this.add(generateHeaderColumnTitle("Device"));
  87. this.add(generateHeaderColumnTitle("Energy"));
  88. this.add(generateHeaderColumnTitle("Priority"));
  89. this.add(generateHeaderColumnTitle("Activ"));
  90. }
  91. private void initSelectAllCheckBox() {
  92. selectAllCheckBox.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
  93. // Pixel Perfect alignment
  94. selectAllCheckBox.setBorder(BorderFactory.createEmptyBorder(2, 3, 0, 0));
  95. selectAllCheckBox.addActionListener(clicked -> {
  96. switch (selectAllCheckBox.getSelectionState()) {
  97. case mid_state_selection:
  98. case selected: {
  99. for (ElementRow row : elementRows) {
  100. row.setSelected(false);
  101. }
  102. duplicateButton.setEnabled(false);
  103. deleteButton.setEnabled(false);
  104. }
  105. break;
  106. case unselected:
  107. if(!elementRows.isEmpty()) {
  108. for (ElementRow row : elementRows) {
  109. row.setSelected(true);
  110. }
  111. duplicateButton.setEnabled(true);
  112. deleteButton.setEnabled(true);
  113. }
  114. default:
  115. break;
  116. }
  117. updateElementSelection();
  118. });
  119. }
  120. private JLabel generateHeaderColumnTitle(String string) {
  121. JLabel columnTitleLabel = new JLabel(string);
  122. columnTitleLabel.setToolTipText(string);
  123. columnTitleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  124. return columnTitleLabel;
  125. }
  126. private void initButtonPanel() {
  127. buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
  128. buttonPanel.add(Box.createRigidArea(new Dimension(2, 0)));
  129. addButton.setIcon(new ImageIcon(ImageImport.loadImage("Images/plus.png", 16, 16)));
  130. addButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  131. addButton.addActionListener(clicked -> {
  132. Optional<HolonObject> last = control.getModel().getSelectedObjects().stream()
  133. .filter(obj -> obj instanceof HolonObject).reduce((prev, next) -> next)
  134. .map(obj -> (HolonObject) obj);
  135. last.ifPresent(obj -> {
  136. obj.addElement(new HolonElement(obj, "Element", 0.0f));
  137. control.calculateStateAndVisualForCurrentTimeStep();
  138. update_ui();
  139. });
  140. });
  141. buttonPanel.add(addButton);
  142. duplicateButton.setIcon(new ImageIcon(ImageImport.loadImage("Images/duplicate.png", 16, 16)));
  143. duplicateButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  144. duplicateButton.addActionListener(clicked -> {
  145. for (ElementRow row : elementRows) {
  146. if(row.isSelected()) {
  147. row.element.parentObject.addElement(new HolonElement(row.element));
  148. }
  149. }
  150. control.calculateStateAndVisualForCurrentTimeStep();
  151. update_ui();
  152. });
  153. buttonPanel.add(duplicateButton);
  154. deleteButton.setIcon(new ImageIcon(ImageImport.loadImage("Images/minus.png", 16, 16)));
  155. deleteButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  156. deleteButton.addActionListener(clicked -> {
  157. for (ElementRow row : elementRows) {
  158. if(row.isSelected()) {
  159. row.element.parentObject.removeElement(row.element);
  160. }
  161. }
  162. control.calculateStateAndVisualForCurrentTimeStep();
  163. update_ui();
  164. });
  165. buttonPanel.add(deleteButton);
  166. }
  167. private void update_ui() {
  168. Stream<HolonElement> elements = extractElements(control.getModel().getSelectedObjects());
  169. // TODO Pooling
  170. this.removeAll();
  171. elementRows.clear();
  172. generateHeader();
  173. elements.forEach(element -> elementRows.add(new ElementRow(element)));
  174. this.add(buttonPanel, "span");
  175. boolean isAtLeastOneHolonObjectSelected =
  176. control.getModel().getSelectedObjects().stream().anyMatch(object -> object instanceof HolonObject);
  177. this.addButton.setEnabled(isAtLeastOneHolonObjectSelected);
  178. duplicateButton.setEnabled(false);
  179. deleteButton.setEnabled(false);
  180. selectAllCheckBox.setSelectionState(State.unselected);
  181. revalidate();
  182. repaint();
  183. this.OnElementSelectionChanged.broadcast(new HashSet<HolonElement>());
  184. }
  185. private void updateElementSelection() {
  186. Set<HolonElement> eleSet = elementRows.stream().filter(ele -> ele.isSelected()).map(row -> row.element).collect(Collectors.toSet());
  187. this.OnElementSelectionChanged.broadcast(eleSet);
  188. }
  189. private void updateButtonAppearance() {
  190. long count = elementRows.stream().filter(ele -> ele.isSelected()).count();
  191. if (count == elementRows.size()) {
  192. selectAllCheckBox.setSelectionState(State.selected);
  193. } else if (count == 0) {
  194. selectAllCheckBox.setSelectionState(State.unselected);
  195. } else {
  196. selectAllCheckBox.setSelectionState(State.mid_state_selection);
  197. }
  198. duplicateButton.setEnabled(count != 0);
  199. deleteButton.setEnabled(count != 0);
  200. }
  201. // Extract elements from a list of AbstractCanvasObjects
  202. static Stream<HolonElement> extractElements(Collection<AbstractCanvasObject> toInspect) {
  203. Stream<HolonElement> recursiveLayer = toInspect.stream().filter(object ->object instanceof GroupNode).flatMap(obj -> extractElements(((GroupNode)obj).getNodes()));
  204. Stream<HolonElement> thisLayer = toInspect.stream().filter(obj -> obj instanceof HolonObject).flatMap(obj -> {
  205. HolonObject ho = (HolonObject) obj;
  206. return ho.getElements().stream();
  207. });
  208. return Stream.concat(thisLayer, recursiveLayer);
  209. }
  210. private class ElementRow {
  211. HolonElement element;
  212. private JCheckBox selectionBox;
  213. Container[] cellsInRow = new Container[7];
  214. ElementRow(HolonElement element) {
  215. this.element = element;
  216. this.createEditFields();
  217. addContainerToInspector();
  218. }
  219. private void addContainerToInspector() {
  220. for (Container cell : cellsInRow) {
  221. InspectorTable.this.add(cell);
  222. }
  223. }
  224. public void setSelected(boolean value) {
  225. selectionBox.setSelected(value);
  226. // Color row
  227. for (Container cell : cellsInRow) {
  228. cell.setBackground(selectionBox.isSelected() ? selectedColor : Color.white);
  229. }
  230. }
  231. public boolean isSelected() {
  232. return selectionBox.isSelected();
  233. }
  234. private void createEditFields() {
  235. // Selected
  236. JPanel selectedColumnPanel = new JPanel(new BorderLayout());
  237. selectedColumnPanel.setBackground(Color.white);
  238. selectedColumnPanel.setBorder(BorderFactory.createLineBorder(borderColor));
  239. selectionBox = new JCheckBox();
  240. selectionBox.addActionListener(clicked -> {
  241. setSelected(selectionBox.isSelected());
  242. updateButtonAppearance();
  243. updateElementSelection();
  244. });
  245. selectedColumnPanel.setMinimumSize(new Dimension(columnHeight, columnHeight));
  246. selectedColumnPanel.setPreferredSize(new Dimension(columnHeight, columnHeight));
  247. selectedColumnPanel.setMaximumSize(new Dimension(columnHeight, columnHeight));
  248. selectionBox.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
  249. selectionBox.setOpaque(false);
  250. selectedColumnPanel.add(selectionBox, BorderLayout.CENTER);
  251. cellsInRow[0] = selectedColumnPanel;
  252. // ObjectName and ID
  253. JTextField objectNameTextField = new JTextField(this.element.parentObject.getName());
  254. objectNameTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
  255. this.element.parentObject.setName(objectNameTextField.getText());
  256. });
  257. objectNameTextField.addActionListener(ae -> update_ui());
  258. cellsInRow[1] = objectNameTextField;
  259. JTextField idObjectTextField = new JTextField(Integer.toString(this.element.parentObject.getId()));
  260. idObjectTextField.setMinimumSize(idObjectTextField.getPreferredSize());
  261. idObjectTextField.setBackground(Color.white);
  262. idObjectTextField.setEditable(false);
  263. idObjectTextField.setEnabled(false);
  264. cellsInRow[2] = idObjectTextField;
  265. // Name
  266. JTextField elementNameTextField = new JTextField(this.element.getEleName());
  267. elementNameTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
  268. this.element.setEleName(elementNameTextField.getText());
  269. });
  270. elementNameTextField.setBackground(Color.white);
  271. cellsInRow[3] = elementNameTextField;
  272. // Energy
  273. JFormattedTextField energyTextField = new JFormattedTextField(doubleFormatter);
  274. energyTextField.setInputVerifier(getInputVerifier());
  275. energyTextField.setValue(this.element.getEnergy());
  276. energyTextField.setBackground(Color.white);
  277. energyTextField.addPropertyChangeListener(actionEvent -> {
  278. try {
  279. float energy = Float.parseFloat(energyTextField.getText());
  280. if (this.element.getEnergy() != energy) {
  281. this.element.setEnergyPerElement(energy);
  282. control.calculateStateAndVisualForCurrentTimeStep();
  283. }
  284. } catch (NumberFormatException e) {
  285. // Dont Update
  286. }
  287. });
  288. cellsInRow[4] = energyTextField;
  289. // Priority
  290. JComboBox<Priority> comboBox = new JComboBox<Priority>(Priority.values());
  291. comboBox.setSelectedItem(this.element.getPriority());
  292. comboBox.setBackground(Color.white);
  293. comboBox.setEditable(false);
  294. comboBox.addActionListener(ae -> {
  295. this.element.setPriority((Priority) comboBox.getSelectedItem());
  296. });
  297. cellsInRow[5] = comboBox;
  298. JPanel checkBoxWrapperPanel = new JPanel(new BorderLayout());
  299. checkBoxWrapperPanel.setBorder(BorderFactory.createLineBorder(borderColor));
  300. checkBoxWrapperPanel.setBackground(Color.white);
  301. checkBoxWrapperPanel.setMinimumSize(new Dimension(columnHeight, columnHeight));
  302. checkBoxWrapperPanel.setMaximumSize(new Dimension(columnHeight, columnHeight));
  303. // Active
  304. JCheckBox checkbox = new JCheckBox();
  305. checkbox.setSelected(this.element.isActive());
  306. checkbox.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
  307. checkbox.setOpaque(false);
  308. checkbox.addActionListener(actionEvent -> {
  309. this.element.setActive(checkbox.isSelected());
  310. control.calculateStateAndVisualForCurrentTimeStep();
  311. });
  312. checkBoxWrapperPanel.add(checkbox, BorderLayout.CENTER);
  313. cellsInRow[6] = checkBoxWrapperPanel;
  314. }
  315. }
  316. }