InspectorTable.java 20 KB

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