1 package fr.ifremer.dali.ui.swing.content.observation.operation.measurement.grouped;
2
3 import fr.ifremer.dali.config.DaliConfiguration;
4 import fr.ifremer.dali.dto.referential.UnitDTO;
5 import fr.ifremer.dali.ui.swing.util.DaliUIs;
6 import fr.ifremer.dali.ui.swing.util.table.DaliPmfmColumnIdentifier;
7 import fr.ifremer.dali.ui.swing.util.table.PmfmTableColumn;
8 import fr.ifremer.quadrige3.ui.swing.table.SwingTable;
9 import org.apache.commons.lang3.StringUtils;
10 import org.jdesktop.swingx.action.AbstractActionExt;
11 import org.jdesktop.swingx.event.TableColumnModelExtListener;
12
13 import javax.swing.*;
14 import javax.swing.event.ChangeEvent;
15 import javax.swing.event.ListSelectionEvent;
16 import javax.swing.event.TableColumnModelEvent;
17 import javax.swing.table.JTableHeader;
18 import javax.swing.table.TableCellRenderer;
19 import javax.swing.table.TableColumn;
20 import javax.swing.table.TableColumnModel;
21 import java.awt.Component;
22 import java.awt.Dimension;
23 import java.awt.event.ActionEvent;
24 import java.beans.PropertyChangeEvent;
25 import java.beans.PropertyChangeListener;
26 import java.math.BigDecimal;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Optional;
31
32 import static org.nuiton.i18n.I18n.t;
33
34
35
36
37
38
39
40 public class OperationMeasurementsTotalFooterAction extends AbstractActionExt {
41
42 private final OperationMeasurementsGroupedTableUIHandler handler;
43 private JLabel label;
44 private FooterTable footer;
45
46 OperationMeasurementsTotalFooterAction(OperationMeasurementsGroupedTableUIHandler handler) {
47 super(t("dali.samplingOperation.measurement.grouped.total"));
48 this.handler = handler;
49 setStateAction();
50 }
51
52 @Override
53 public void actionPerformed(ActionEvent e) {
54 if (isSelected()) {
55 getFooterTable().createCalculators();
56 showTotal();
57 } else {
58 hideTotal();
59 }
60 }
61
62 private void showTotal() {
63
64
65 addIfAbsent(getFooterPanel(), getFooterLabel());
66 getFooterLayout().putConstraint(SpringLayout.NORTH, getFooterLabel(), 0, SpringLayout.NORTH, getFooterPanel());
67 getFooterLayout().putConstraint(SpringLayout.SOUTH, getFooterLabel(), 0, SpringLayout.SOUTH, getFooterPanel());
68 getFooterLayout().putConstraint(SpringLayout.WEST, getFooterLabel(), 0, SpringLayout.WEST, getFooterPanel());
69 getFooterLayout().putConstraint(SpringLayout.EAST, getFooterLabel(), getRowHeaderSize(), SpringLayout.WEST, getFooterPanel());
70
71
72 addIfAbsent(getFooterPanel(), getFooterTable());
73 getFooterLayout().putConstraint(SpringLayout.NORTH, getFooterTable(), 0, SpringLayout.NORTH, getFooterPanel());
74 getFooterLayout().putConstraint(SpringLayout.SOUTH, getFooterTable(), 0, SpringLayout.SOUTH, getFooterPanel());
75 getFooterLayout().putConstraint(SpringLayout.WEST, getFooterTable(), getScrollPosition(), SpringLayout.WEST, getFooterPanel());
76 getFooterLayout().putConstraint(SpringLayout.EAST, getFooterTable(), 0, SpringLayout.EAST, getFooterPanel());
77
78
79 getFooterTable().setPreferredSize(new Dimension(getFooterTable().getPreferredSize().width, getFooterLabel().getPreferredSize().height));
80 getFooterPanel().setPreferredSize(getFooterTable().getPreferredSize());
81
82 SwingUtilities.invokeLater(getFooterPanel()::revalidate);
83
84 }
85
86 private JLabel getFooterLabel() {
87 if (label == null) {
88 label = new JLabel(t("dali.samplingOperation.measurement.grouped.total.label") + " ");
89 label.setHorizontalAlignment(SwingConstants.RIGHT);
90
91 label.setForeground(Optional.ofNullable(UIManager.getColor("thematicLabelColor")).orElse(label.getForeground()));
92
93 label.setBackground(getTableScrollPane().getBackground());
94 label.setOpaque(true);
95
96
97 label.setPreferredSize(new Dimension(label.getPreferredSize().width, DaliUIs.DALI_COMPONENT_HEIGHT));
98 }
99 return label;
100 }
101
102 private FooterTable getFooterTable() {
103 if (footer == null) {
104 footer = new FooterTable(getTable().getColumnModel());
105 footer.setTable(new JTable(null, getTable().getColumnModel()));
106 initListeners();
107 }
108 return footer;
109 }
110
111 private void initListeners() {
112
113
114 getTable().getColumnModel().addColumnModelListener(new TableColumnModelExtListener() {
115
116 @Override
117 public void columnAdded(TableColumnModelEvent e) {
118 updateUI();
119 }
120
121 @Override
122 public void columnRemoved(TableColumnModelEvent e) {
123 updateUI();
124 }
125
126 @Override
127 public void columnMoved(TableColumnModelEvent e) {
128 updateUI();
129 }
130
131 @Override
132 public void columnMarginChanged(ChangeEvent e) {
133 updateUI();
134 }
135
136 @Override
137 public void columnSelectionChanged(ListSelectionEvent e) {
138
139 }
140
141 @Override
142 public void columnPropertyChange(PropertyChangeEvent event) {
143
144 }
145 });
146
147
148 getTableScrollPane().getViewport().addChangeListener(e -> updateUI());
149
150
151 getTable().addPropertyChangeListener(SwingTable.PROPERTY_ROW_FILTER, evt -> updateTotals());
152
153
154 getTableModel().addTableModelListener(evt -> updateUI());
155
156 }
157
158 private void addIfAbsent(JPanel panel, Component field) {
159 if (Arrays.stream(panel.getComponents()).noneMatch(component -> component == field)) panel.add(field);
160 }
161
162 private int getScrollPosition() {
163 int scrollPosition = getTableScrollPane().getViewport().getViewPosition().x;
164
165
166 scrollPosition -= getRowHeaderSize();
167
168 return -scrollPosition;
169 }
170
171 private int getRowHeaderSize() {
172 if (getTableScrollPane().getRowHeader() != null) {
173 return getTableScrollPane().getRowHeader().getViewSize().width;
174 }
175 return 0;
176 }
177
178 private void hideTotal() {
179
180 getFooterPanel().remove(getFooterTable());
181
182
183 getFooterPanel().setPreferredSize(new Dimension(0, 0));
184
185 SwingUtilities.invokeLater(getFooterPanel()::revalidate);
186
187 }
188
189 private void updateUI() {
190 if (isSelected())
191 SwingUtilities.invokeLater(() -> {
192 getFooterTable().createCalculators();
193 showTotal();
194 });
195 }
196
197 private void updateTotals() {
198 if (isSelected()) {
199 getFooterTable().updateTotals();
200 getFooterTable().repaint();
201 }
202 }
203
204 private DaliConfiguration getConfig() {
205 return handler.getConfig();
206 }
207
208 private SwingTable getTable() {
209 return handler.getTable();
210 }
211
212 private OperationMeasurementsGroupedTableModel getTableModel() {
213 return handler.getTableModel();
214 }
215
216 private JScrollPane getTableScrollPane() {
217 return (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, getTable());
218 }
219
220 private JPanel getFooterPanel() {
221 return handler.getUI().getFooterPanel();
222 }
223
224 private SpringLayout getFooterLayout() {
225 return handler.getUI().getFooterLayout();
226 }
227
228 private class FooterTable extends JTableHeader {
229
230 private Map<Integer, TotalCalculator> calculators = null;
231
232 FooterTable(TableColumnModel columnModel) {
233 super(columnModel);
234
235 setDefaultRenderer(new FooterRenderer(getDefaultRenderer(), this));
236 }
237
238 public void createCalculators() {
239 calculators = new HashMap<>();
240
241 for (int i = 0; i < getColumnModel().getColumnCount(); i++) {
242 TableColumn column = getColumnModel().getColumn(i);
243 if (column instanceof PmfmTableColumn) {
244 PmfmTableColumn pmfmColumn = (PmfmTableColumn) column;
245 if (pmfmColumn.isNumerical()) {
246 calculators.put(pmfmColumn.getModelIndex(), new TotalCalculator(pmfmColumn.getPmfmIdentifier()));
247 }
248 }
249 }
250 }
251
252 public void updateTotals() {
253 if (calculators == null)
254 createCalculators();
255 if (calculators.isEmpty())
256 return;
257 calculators.values().forEach(TotalCalculator::calculate);
258 }
259
260 public TotalCalculator getCalculator(int index) {
261 return calculators.get(index);
262 }
263 }
264
265 private class FooterRenderer implements TableCellRenderer {
266
267 private final TableCellRenderer delegate;
268 private final FooterTable footer;
269
270 FooterRenderer(TableCellRenderer delegate, FooterTable footer) {
271 this.delegate = delegate;
272 this.footer = footer;
273 }
274
275 @Override
276 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
277 Component component = delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
278
279 if (component instanceof JLabel) {
280 JLabel label = (JLabel) component;
281 label.setHorizontalAlignment(SwingConstants.RIGHT);
282 if (getColumn(column) instanceof PmfmTableColumn) {
283 PmfmTableColumn pmfmTableColumn = (PmfmTableColumn) getColumn(column);
284 TotalCalculator calculator = getCalculator(column);
285 if (calculator != null) {
286 UnitDTO unit = pmfmTableColumn.getPmfmIdentifier().getPmfm().getUnit();
287 String unitSymbol = (unit == null || getConfig().getExtractionUnitIdsToIgnore().contains(unit.getId())) ? "" : " " + unit.getSymbol();
288 label.setText(StringUtils.isNotBlank(calculator.getValue()) ? calculator.getValue() + unitSymbol : "");
289 } else {
290 label.setText("");
291 }
292 } else {
293 label.setText("");
294 }
295 } else {
296 component = new JLabel();
297 }
298 return component;
299 }
300
301 private TableColumn getColumn(int viewColumnIndex) {
302 return footer.getColumnModel().getColumn(viewColumnIndex);
303 }
304
305 private TotalCalculator getCalculator(int viewColumnIndex) {
306 return footer.getCalculator(getColumn(viewColumnIndex).getModelIndex());
307 }
308
309 }
310
311 private class TotalCalculator implements PropertyChangeListener {
312
313 private final DaliPmfmColumnIdentifier<OperationMeasurementsGroupedRowModel> identifier;
314 private String value;
315
316 TotalCalculator(DaliPmfmColumnIdentifier<OperationMeasurementsGroupedRowModel> identifier) {
317 this.identifier = identifier;
318
319
320 identifier.addPropertyChangeListener(this);
321
322 calculate();
323 }
324
325 void calculate() {
326 BigDecimal total = new BigDecimal(0);
327 boolean add = false;
328 for (int i = 0; i < getTable().getRowCount(); i++) {
329 Object value = identifier.getValue(getTableModel().getEntry(getTable().convertRowIndexToModel(i)));
330 if (value instanceof BigDecimal) {
331 total = total.add((BigDecimal) value);
332 add = true;
333 }
334 }
335 value = add ? total.toString() : "";
336 }
337
338 String getValue() {
339 return value;
340 }
341
342 @Override
343 public void propertyChange(PropertyChangeEvent evt) {
344
345 if (evt instanceof DaliPmfmColumnIdentifier.PmfmChangeEvent) {
346 calculate();
347 getFooterTable().repaint();
348 }
349 }
350 }
351 }
352