InspectorTable.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. package holeg.ui.view.inspector;
  2. import holeg.model.AbstractCanvasObject;
  3. import holeg.model.GroupNode;
  4. import holeg.model.HolonElement;
  5. import holeg.model.HolonElement.Priority;
  6. import holeg.model.HolonObject;
  7. import holeg.preferences.ColorPreference;
  8. import holeg.preferences.ImagePreference;
  9. import holeg.ui.controller.Control;
  10. import holeg.ui.model.GuiSettings;
  11. import holeg.ui.view.component.TrippleCheckBox;
  12. import holeg.ui.view.component.TrippleCheckBox.State;
  13. import holeg.ui.view.image.Import;
  14. import holeg.utility.events.Action;
  15. import holeg.utility.listener.SimpleDocumentListener;
  16. import holeg.utility.pooling.Pool;
  17. import java.awt.BorderLayout;
  18. import java.awt.Color;
  19. import java.awt.Container;
  20. import java.awt.Dimension;
  21. import java.awt.event.ActionEvent;
  22. import java.awt.event.InputEvent;
  23. import java.awt.event.KeyEvent;
  24. import java.math.RoundingMode;
  25. import java.text.NumberFormat;
  26. import java.util.ArrayList;
  27. import java.util.Collection;
  28. import java.util.Comparator;
  29. import java.util.HashSet;
  30. import java.util.List;
  31. import java.util.Locale;
  32. import java.util.Optional;
  33. import java.util.Set;
  34. import java.util.logging.Logger;
  35. import java.util.stream.Collectors;
  36. import java.util.stream.Stream;
  37. import javax.swing.AbstractAction;
  38. import javax.swing.BorderFactory;
  39. import javax.swing.Box;
  40. import javax.swing.BoxLayout;
  41. import javax.swing.ImageIcon;
  42. import javax.swing.JButton;
  43. import javax.swing.JCheckBox;
  44. import javax.swing.JComboBox;
  45. import javax.swing.JFormattedTextField;
  46. import javax.swing.JLabel;
  47. import javax.swing.JPanel;
  48. import javax.swing.JTextField;
  49. import javax.swing.KeyStroke;
  50. import javax.swing.SwingConstants;
  51. import javax.swing.text.NumberFormatter;
  52. import net.miginfocom.swing.MigLayout;
  53. /**
  54. * This class shows all HolonElements of the selected HolonObjects.
  55. */
  56. public class InspectorTable extends JPanel {
  57. private static final Logger log = Logger.getLogger(InspectorTable.class.getName());
  58. private final static NumberFormatter doubleFormatter = generateNumberFormatter();
  59. private final Pool<ElementRow> rowPool = new Pool<>() {
  60. @Override
  61. public ElementRow create() {
  62. return new ElementRow();
  63. }
  64. };
  65. private final int maxDisplayedRowsNumber = 100;
  66. private final Control control;
  67. // UI
  68. private final TrippleCheckBox selectAllCheckBox = new TrippleCheckBox();
  69. private final JButton addButton = new JButton();
  70. private final JButton duplicateButton = new JButton();
  71. private final JButton deleteButton = new JButton();
  72. private final JPanel buttonPanel = new JPanel();
  73. private final JButton pageIncreaseButton = new JButton();
  74. private final JButton pageDecreaseButton = new JButton();
  75. private final JLabel pageInformationLabel = new JLabel();
  76. private final JPanel pageSelectionPanel = new JPanel();
  77. private final ArrayList<SortButton<ElementRow>> headerButtonList = new ArrayList<>();
  78. // Events
  79. public Action<Set<HolonElement>> OnElementSelectionChanged = new Action<>();
  80. private int actualPage = 0;
  81. private int maxPageNumberForThisSelection = 0;
  82. // Colors
  83. // sorting
  84. private Comparator<ElementRow> actual_comp = (ElementRow a, ElementRow b) -> Float.compare(
  85. a.element.getEnergy(),
  86. b.element.getEnergy());
  87. private Thread populateRowsThread;
  88. private boolean abortThread = false;
  89. public InspectorTable(Control control) {
  90. control.OnSelectionChanged.addListener(this::updateInspectorUi);
  91. this.control = control;
  92. init();
  93. addHeader();
  94. }
  95. private static NumberFormatter generateNumberFormatter() {
  96. NumberFormat doubleFormat = NumberFormat.getNumberInstance(Locale.US);
  97. doubleFormat.setMinimumFractionDigits(1);
  98. doubleFormat.setMaximumFractionDigits(10);
  99. doubleFormat.setRoundingMode(RoundingMode.HALF_UP);
  100. doubleFormat.setGroupingUsed(false);
  101. NumberFormatter doubleFormatter = new NumberFormatter(doubleFormat);
  102. doubleFormatter.setCommitsOnValidEdit(true);
  103. doubleFormatter.setValueClass(Double.class);
  104. return doubleFormatter;
  105. }
  106. // Extract elements from a list of AbstractCanvasObjects
  107. static Stream<HolonElement> extractElements(Collection<AbstractCanvasObject> toInspect) {
  108. Stream<HolonElement> recursiveLayer = toInspect.stream()
  109. .filter(object -> object instanceof GroupNode).flatMap(
  110. obj -> ((GroupNode) obj).getAllHolonObjectsRecursive()
  111. .flatMap(HolonObject::elementsStream));
  112. Stream<HolonElement> thisLayer = toInspect.stream().filter(obj -> obj instanceof HolonObject)
  113. .flatMap(obj -> {
  114. HolonObject ho = (HolonObject) obj;
  115. return ho.elementsStream();
  116. });
  117. return Stream.concat(thisLayer, recursiveLayer);
  118. }
  119. private void init() {
  120. MigLayout layout = new MigLayout("insets 0,gap 0,wrap 7", // Layout Constraints
  121. "[][fill, grow][fill][fill, grow][fill, grow][][fill]", // Column constraints
  122. "[25!][20:20:20]"); // Row constraints
  123. this.setLayout(layout);
  124. initSelectAllCheckBox();
  125. initButtons();
  126. initKeyControls();
  127. initHeaderButtons();
  128. }
  129. private void initKeyControls() {
  130. this.getInputMap(WHEN_IN_FOCUSED_WINDOW)
  131. .put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.ALT_DOWN_MASK), "PageRight");
  132. this.getInputMap(WHEN_IN_FOCUSED_WINDOW)
  133. .put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.ALT_DOWN_MASK),
  134. "PageLeft");
  135. this.getActionMap().put("PageRight", new AbstractAction() {
  136. @Override
  137. public void actionPerformed(ActionEvent e) {
  138. performPageAction(PageAction.Increase);
  139. }
  140. });
  141. this.getActionMap().put("PageLeft", new AbstractAction() {
  142. @Override
  143. public void actionPerformed(ActionEvent e) {
  144. performPageAction(PageAction.Decrease);
  145. }
  146. });
  147. }
  148. private void performPageAction(PageAction action) {
  149. int newPageNumber = switch (action) {
  150. case Decrease -> Math.max(actualPage - 1, 0);
  151. default -> Math.min(actualPage + 1, maxPageNumberForThisSelection);
  152. };
  153. if (newPageNumber != actualPage) {
  154. actualPage = newPageNumber;
  155. updateTableUi();
  156. updatePageButtonAppearance();
  157. }
  158. }
  159. private void updatePageButtonAppearance() {
  160. this.pageDecreaseButton.setEnabled(actualPage != 0);
  161. this.pageIncreaseButton.setEnabled(actualPage != maxPageNumberForThisSelection);
  162. }
  163. private void initHeaderButtons() {
  164. Comparator<ElementRow> objectComp = Comparator.comparing(
  165. (ElementRow a) -> a.element.parentObject.getName());
  166. Comparator<ElementRow> idComp = Comparator.comparingInt(
  167. (ElementRow a) -> a.element.parentObject.getId());
  168. Comparator<ElementRow> deviceComp = Comparator.comparing((ElementRow a) -> a.element.getName());
  169. Comparator<ElementRow> energyComp = (ElementRow a, ElementRow b) -> Float.compare(
  170. a.element.getEnergy(),
  171. b.element.getEnergy());
  172. Comparator<ElementRow> priorityComp = Comparator.comparing(
  173. (ElementRow a) -> a.element.getPriority());
  174. Comparator<ElementRow> activeComp = (ElementRow a, ElementRow b) -> Boolean.compare(
  175. a.element.active,
  176. b.element.active);
  177. headerButtonList.add(new SortButton<>("Object", objectComp));
  178. headerButtonList.add(new SortButton<>("Id", idComp));
  179. headerButtonList.add(new SortButton<>("Device", deviceComp));
  180. headerButtonList.add(new SortButton<>("Energy", energyComp));
  181. headerButtonList.add(new SortButton<>("Priority", priorityComp));
  182. headerButtonList.add(new SortButton<>("Active", activeComp));
  183. }
  184. private void addHeader() {
  185. this.add(selectAllCheckBox);
  186. for (SortButton<ElementRow> button : headerButtonList) {
  187. this.add(button);
  188. }
  189. }
  190. private void initSelectAllCheckBox() {
  191. selectAllCheckBox.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
  192. // Pixel Perfect alignment
  193. selectAllCheckBox.setBorder(BorderFactory.createEmptyBorder(2, 3, 0, 0));
  194. selectAllCheckBox.addActionListener(clicked -> selectAllChanges(selectAllCheckBox.getSelectionState()));
  195. }
  196. private void selectAllChanges(TrippleCheckBox.State oldState) {
  197. switch (oldState) {
  198. case mid_state_selection:
  199. case selected: {
  200. rowPool.getBorrowedStream().forEach(row -> row.setSelected(false));
  201. duplicateButton.setEnabled(false);
  202. deleteButton.setEnabled(false);
  203. }
  204. break;
  205. case unselected:
  206. if (rowPool.getBorrowedCount() != 0) {
  207. rowPool.getBorrowedStream().forEach(row -> row.setSelected(true));
  208. duplicateButton.setEnabled(true);
  209. deleteButton.setEnabled(true);
  210. }
  211. default:
  212. break;
  213. }
  214. updateElementSelection();
  215. }
  216. private void initButtons() {
  217. buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
  218. buttonPanel.add(Box.createRigidArea(new Dimension(2, 0)));
  219. addButton.setIcon(
  220. new ImageIcon(Import.loadImage(ImagePreference.Button.Inspector.Add, 16, 16)));
  221. addButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  222. addButton.addActionListener(clicked -> {
  223. Optional<HolonObject> last = GuiSettings.getSelectedObjects().stream()
  224. .filter(obj -> obj instanceof HolonObject).reduce((prev, next) -> next)
  225. .map(obj -> (HolonObject) obj);
  226. last.ifPresent(obj -> {
  227. obj.add(new HolonElement(obj, "Element", 0.0f));
  228. control.updateStateForCurrentIteration();
  229. control.OnSelectionChanged.broadcast();
  230. });
  231. });
  232. buttonPanel.add(addButton);
  233. duplicateButton.setIcon(
  234. new ImageIcon(Import.loadImage(ImagePreference.Button.Inspector.Duplicate, 16, 16)));
  235. duplicateButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  236. duplicateButton.addActionListener(clicked -> {
  237. rowPool.getBorrowedStream().forEach(row -> {
  238. if (row.isSelected()) {
  239. row.element.parentObject.add(new HolonElement(row.element));
  240. }
  241. });
  242. control.updateStateForCurrentIteration();
  243. control.OnSelectionChanged.broadcast();
  244. });
  245. buttonPanel.add(duplicateButton);
  246. deleteButton.setIcon(
  247. new ImageIcon(Import.loadImage(ImagePreference.Button.Inspector.Remove, 16, 16)));
  248. deleteButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  249. deleteButton.addActionListener(clicked -> {
  250. log.info("DeleteButton");
  251. rowPool.getBorrowedStream().forEach(row -> {
  252. if (row.isSelected()) {
  253. row.element.parentObject.remove(row.element);
  254. }
  255. });
  256. log.info("row deleted");
  257. control.updateStateForCurrentIteration();
  258. log.info("updated");
  259. control.OnSelectionChanged.broadcast();
  260. log.info("selectionChanged");
  261. });
  262. buttonPanel.add(deleteButton);
  263. pageIncreaseButton.setIcon(
  264. new ImageIcon(Import.loadImage("images/buttons/page_increase.png", 16, 16)));
  265. pageIncreaseButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  266. pageIncreaseButton.addActionListener(clicked -> this.performPageAction(PageAction.Increase));
  267. pageDecreaseButton.setIcon(
  268. new ImageIcon(Import.loadImage("images/buttons/page_decrease.png", 16, 16)));
  269. pageDecreaseButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  270. pageDecreaseButton.addActionListener(clicked -> this.performPageAction(PageAction.Decrease));
  271. pageInformationLabel.setForeground(Color.gray);
  272. pageSelectionPanel.setLayout(new BoxLayout(pageSelectionPanel, BoxLayout.LINE_AXIS));
  273. pageSelectionPanel.add(Box.createRigidArea(new Dimension(2, 0)));
  274. pageSelectionPanel.add(this.pageInformationLabel);
  275. pageSelectionPanel.add(Box.createHorizontalGlue());
  276. pageSelectionPanel.add(this.pageDecreaseButton);
  277. pageSelectionPanel.add(this.pageIncreaseButton);
  278. }
  279. private void assignElementsToRowPool(Set<AbstractCanvasObject> selection) {
  280. List<HolonElement> elementList = extractElements(selection).toList();
  281. rowPool.getBorrowedStream().forEach(InspectorTable.ElementRow::clear);
  282. rowPool.clear();
  283. for (HolonElement element : elementList) {
  284. ElementRow row = rowPool.get();
  285. row.setElement(element);
  286. }
  287. actualPage = 0;
  288. this.maxPageNumberForThisSelection = elementList.size() / this.maxDisplayedRowsNumber;
  289. updatePageButtonAppearance();
  290. }
  291. private void updateTableUi() {
  292. // Maybe abort current thread and join them
  293. if (populateRowsThread != null) {
  294. try {
  295. abortThread = true;
  296. populateRowsThread.join();
  297. abortThread = false;
  298. } catch (InterruptedException e) {
  299. e.printStackTrace();
  300. }
  301. }
  302. populateRowsThread = new Thread(() -> {
  303. int numberOfRows = rowPool.getBorrowedCount();
  304. this.removeAll();
  305. addHeader();
  306. rowPool.getBorrowedStream().sorted(actual_comp)
  307. .skip((long) actualPage * maxDisplayedRowsNumber)
  308. .limit(maxDisplayedRowsNumber).takeWhile(row -> !abortThread)
  309. .forEach(ElementRow::addContainerToInspector);
  310. if (numberOfRows > maxDisplayedRowsNumber) {
  311. int lastDisplayedElementNumber = Math.min(numberOfRows,
  312. (actualPage + 1) * maxDisplayedRowsNumber);
  313. pageInformationLabel.setText(
  314. String.format("%d - %d from %d", 1 + actualPage * maxDisplayedRowsNumber,
  315. lastDisplayedElementNumber, numberOfRows));
  316. this.add(pageSelectionPanel, "span, grow");
  317. }
  318. this.add(buttonPanel, "span");
  319. boolean isAtLeastOneHolonObjectSelected = GuiSettings.getSelectedObjects().stream()
  320. .anyMatch(object -> object instanceof HolonObject);
  321. this.addButton.setEnabled(isAtLeastOneHolonObjectSelected);
  322. duplicateButton.setEnabled(false);
  323. deleteButton.setEnabled(false);
  324. this.OnElementSelectionChanged.broadcast(new HashSet<>());
  325. selectAllCheckBox.setSelectionState(State.selected);
  326. selectAllChanges(State.unselected);
  327. revalidate();
  328. repaint();
  329. });
  330. populateRowsThread.start();
  331. }
  332. private void updateInspectorUi() {
  333. // clone for concurrency
  334. Set<AbstractCanvasObject> selection = new HashSet<>(GuiSettings.getSelectedObjects());
  335. assignElementsToRowPool(selection);
  336. updateTableUi();
  337. }
  338. private void updateElementSelection() {
  339. Set<HolonElement> eleSet = rowPool.getBorrowedStream().filter(ElementRow::isSelected)
  340. .map(row -> row.element)
  341. .collect(Collectors.toSet());
  342. this.OnElementSelectionChanged.broadcast(eleSet);
  343. }
  344. private void updateButtonAppearance() {
  345. long count = rowPool.getBorrowedStream().filter(ElementRow::isSelected).count();
  346. if (count == rowPool.getBorrowedCount()) {
  347. selectAllCheckBox.setSelectionState(State.selected);
  348. } else if (count == 0) {
  349. selectAllCheckBox.setSelectionState(State.unselected);
  350. } else {
  351. selectAllCheckBox.setSelectionState(State.mid_state_selection);
  352. }
  353. duplicateButton.setEnabled(count != 0);
  354. deleteButton.setEnabled(count != 0);
  355. }
  356. private enum PageAction {
  357. Increase, Decrease
  358. }
  359. private enum SortState {
  360. None, Descending, Ascending
  361. }
  362. /**
  363. * An ElementRow is a container to access an HolonElement with the right manipulation.
  364. */
  365. private class ElementRow {
  366. private final Container[] cellsInRow = new Container[7];
  367. private HolonElement element = null;
  368. // TextBoxes
  369. private JTextField elementNameTextField;
  370. private JCheckBox selectionBox;
  371. private JTextField idObjectTextField;
  372. private JFormattedTextField energyTextField;
  373. private JComboBox<Priority> comboBox;
  374. private JCheckBox activeCheckBox;
  375. private JTextField objectNameTextField;
  376. public ElementRow() {
  377. this.createEditFields();
  378. }
  379. public void addContainerToInspector() {
  380. for (Container cell : cellsInRow) {
  381. InspectorTable.this.add(cell);
  382. }
  383. }
  384. public boolean isSelected() {
  385. return selectionBox.isSelected();
  386. }
  387. public void setSelected(boolean value) {
  388. selectionBox.setSelected(value);
  389. // Color row
  390. for (Container cell : cellsInRow) {
  391. cell.setBackground(
  392. selectionBox.isSelected() ? ColorPreference.Inspector.Selected : Color.white);
  393. }
  394. }
  395. /**
  396. * Connects the ElementRow with a HolonElement.
  397. */
  398. public void setElement(HolonElement element) {
  399. objectNameTextField.setText(element.parentObject.getName());
  400. idObjectTextField.setText(Integer.toString(element.parentObject.getId()));
  401. elementNameTextField.setText(element.getName());
  402. comboBox.setSelectedItem(element.getPriority());
  403. activeCheckBox.setSelected(element.active);
  404. energyTextField.setValue(element.getEnergy());
  405. this.element = element;
  406. setSelected(false);
  407. }
  408. /**
  409. * Removes the connection to the HolonElement.
  410. */
  411. public void clear() {
  412. this.element = null;
  413. }
  414. private void createEditFields() {
  415. // Selected
  416. JPanel selectedColumnPanel = new JPanel(new BorderLayout());
  417. selectedColumnPanel.setBackground(Color.white);
  418. selectedColumnPanel.setBorder(
  419. BorderFactory.createLineBorder(ColorPreference.Inspector.Border));
  420. selectionBox = new JCheckBox();
  421. selectionBox.addActionListener(clicked -> {
  422. setSelected(selectionBox.isSelected());
  423. updateButtonAppearance();
  424. updateElementSelection();
  425. });
  426. int columnHeight = 20;
  427. selectedColumnPanel.setMinimumSize(new Dimension(columnHeight, columnHeight));
  428. selectedColumnPanel.setPreferredSize(new Dimension(columnHeight, columnHeight));
  429. selectedColumnPanel.setMaximumSize(new Dimension(columnHeight, columnHeight));
  430. selectionBox.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
  431. selectionBox.setOpaque(false);
  432. selectedColumnPanel.add(selectionBox, BorderLayout.CENTER);
  433. cellsInRow[0] = selectedColumnPanel;
  434. // ObjectName and ID
  435. objectNameTextField = new JTextField();
  436. objectNameTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
  437. if (this.element != null) {
  438. this.element.parentObject.setName(objectNameTextField.getText());
  439. }
  440. });
  441. objectNameTextField.addActionListener(ae -> updateInspectorUi());
  442. cellsInRow[1] = objectNameTextField;
  443. idObjectTextField = new JTextField();
  444. idObjectTextField.setMinimumSize(idObjectTextField.getPreferredSize());
  445. idObjectTextField.setBackground(Color.white);
  446. idObjectTextField.setEditable(false);
  447. idObjectTextField.setEnabled(false);
  448. cellsInRow[2] = idObjectTextField;
  449. // Name
  450. elementNameTextField = new JTextField();
  451. elementNameTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
  452. if (this.element != null) {
  453. this.element.setName(elementNameTextField.getText());
  454. }
  455. });
  456. elementNameTextField.setBackground(Color.white);
  457. cellsInRow[3] = elementNameTextField;
  458. // Energy
  459. energyTextField = new JFormattedTextField(doubleFormatter);
  460. energyTextField.setInputVerifier(getInputVerifier());
  461. energyTextField.setBackground(Color.white);
  462. energyTextField.addPropertyChangeListener(actionEvent -> {
  463. try {
  464. float energy = Float.parseFloat(energyTextField.getText());
  465. if (this.element != null && this.element.getEnergy() != energy) {
  466. this.element.setEnergy(energy);
  467. control.updateStateForCurrentIteration();
  468. }
  469. } catch (NumberFormatException e) {
  470. // Dont Update
  471. }
  472. });
  473. cellsInRow[4] = energyTextField;
  474. // Priority
  475. comboBox = new JComboBox<>(Priority.values());
  476. comboBox.setBackground(Color.white);
  477. comboBox.setEditable(false);
  478. comboBox.addActionListener(ae -> {
  479. if (this.element != null) {
  480. this.element.setPriority((Priority) comboBox.getSelectedItem());
  481. control.updateStateForCurrentIteration();
  482. }
  483. });
  484. cellsInRow[5] = comboBox;
  485. JPanel checkBoxWrapperPanel = new JPanel(new BorderLayout());
  486. checkBoxWrapperPanel.setBorder(
  487. BorderFactory.createLineBorder(ColorPreference.Inspector.Border));
  488. checkBoxWrapperPanel.setBackground(Color.white);
  489. checkBoxWrapperPanel.setMinimumSize(new Dimension(columnHeight, columnHeight));
  490. checkBoxWrapperPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, columnHeight));
  491. // Active
  492. activeCheckBox = new JCheckBox();
  493. activeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
  494. activeCheckBox.setOpaque(false);
  495. activeCheckBox.addActionListener(actionEvent -> {
  496. if (this.element != null) {
  497. this.element.active = activeCheckBox.isSelected();
  498. control.updateStateForCurrentIteration();
  499. }
  500. });
  501. checkBoxWrapperPanel.add(activeCheckBox, BorderLayout.CENTER);
  502. cellsInRow[6] = checkBoxWrapperPanel;
  503. }
  504. }
  505. /**
  506. * This class handles the Sorting by tapping on the header of the InspectorTable.
  507. * @param <T>
  508. */
  509. private class SortButton<T> extends JButton {
  510. private final Comparator<T> comp;
  511. private SortState state = SortState.None;
  512. public SortButton(String text, Comparator<T> comp) {
  513. super(text);
  514. this.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  515. this.setContentAreaFilled(false);
  516. this.setBorderPainted(false);
  517. this.setFocusPainted(false);
  518. this.setHorizontalAlignment(SwingConstants.LEFT);
  519. this.comp = comp;
  520. this.addActionListener(onClick -> changeStateOnClick());
  521. }
  522. @SuppressWarnings("unchecked")
  523. private void changeStateOnClick() {
  524. setState((this.state == SortState.Ascending) ? SortState.Descending : SortState.Ascending);
  525. headerButtonList.stream().filter(button -> (button != this))
  526. .forEach(button -> button.setState(SortState.None));
  527. actual_comp = (Comparator<ElementRow>) getComp();
  528. updateInspectorUi();
  529. }
  530. public void setState(SortState state) {
  531. this.state = state;
  532. String text = this.getText();
  533. // remove order symbols from text
  534. text = text.replaceAll("[\u25bc\u25b2]", "");
  535. // update text
  536. switch (state) {
  537. case Descending -> this.setText(text + "\u25bc");
  538. case Ascending -> this.setText(text + "\u25b2");
  539. default -> this.setText(text);
  540. }
  541. }
  542. public Comparator<T> getComp() {
  543. return switch (state) {
  544. case Descending -> comp.reversed();
  545. default -> comp;
  546. };
  547. }
  548. }
  549. }