InspectorTable.java 20 KB

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