1 package com.jsql.view.swing.table;
2
3 import javax.swing.*;
4 import javax.swing.event.TableModelEvent;
5 import javax.swing.event.TableModelListener;
6 import javax.swing.table.TableCellRenderer;
7 import javax.swing.table.TableColumn;
8 import javax.swing.table.TableColumnModel;
9 import javax.swing.table.TableModel;
10 import java.awt.*;
11 import java.awt.event.ActionEvent;
12 import java.beans.PropertyChangeEvent;
13 import java.beans.PropertyChangeListener;
14 import java.util.HashMap;
15 import java.util.Map;
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 public class AdjusterTableColumn implements PropertyChangeListener, TableModelListener {
32
33 private final JTable tableAdjust;
34 private final int spacing;
35 private boolean isColumnHeaderIncluded;
36 private boolean isColumnDataIncluded;
37 private boolean isOnlyAdjustLarger;
38 private boolean isDynamicAdjustment;
39 private final Map<TableColumn, Integer> columnSizes = new HashMap<>();
40
41
42
43
44 public AdjusterTableColumn(JTable table) {
45 this(table, 6);
46 }
47
48
49
50
51 public AdjusterTableColumn(JTable tableAdjust, int spacing) {
52 this.tableAdjust = tableAdjust;
53 this.spacing = spacing;
54 this.setColumnHeaderIncluded(true);
55 this.setColumnDataIncluded(true);
56 this.setOnlyAdjustLarger(true);
57 this.setDynamicAdjustment(false);
58 this.installActions();
59 }
60
61
62
63
64 public void adjustColumns() {
65 TableColumnModel tcm = this.tableAdjust.getColumnModel();
66 for (var i = 0 ; i < tcm.getColumnCount() ; i++) {
67 this.adjustColumn(i);
68 }
69 }
70
71
72
73
74 public void adjustColumn(final int column) {
75 var tableColumn = this.tableAdjust.getColumnModel().getColumn(column);
76 if (!tableColumn.getResizable()) {
77 return;
78 }
79
80 int columnHeaderWidth = this.getColumnHeaderWidth(column);
81 int columnDataWidth = this.getColumnDataWidth(column);
82 int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);
83 this.updateTableColumn(column, preferredWidth);
84 }
85
86
87
88
89 private int getColumnHeaderWidth(int column) {
90 if (!this.isColumnHeaderIncluded) {
91 return 0;
92 }
93
94 var tableColumn = this.tableAdjust.getColumnModel().getColumn(column);
95 Object value = tableColumn.getHeaderValue();
96 TableCellRenderer renderer = tableColumn.getHeaderRenderer();
97 if (renderer == null) {
98 renderer = this.tableAdjust.getTableHeader().getDefaultRenderer();
99 }
100
101 var c = renderer.getTableCellRendererComponent(this.tableAdjust, value, false, false, -1, column);
102 return c.getPreferredSize().width;
103 }
104
105
106
107
108
109 private int getColumnDataWidth(int column) {
110 if (!this.isColumnDataIncluded) {
111 return 0;
112 }
113
114 var preferredWidth = 0;
115 int maxWidth = this.tableAdjust.getColumnModel().getColumn(column).getMaxWidth();
116
117 for (var row = 0 ; row < this.tableAdjust.getRowCount() ; row++) {
118 preferredWidth = Math.max(preferredWidth, this.getCellDataWidth(row, column));
119
120 if (preferredWidth >= maxWidth) {
121 break;
122 }
123 }
124 return preferredWidth;
125 }
126
127
128
129
130 private int getCellDataWidth(int row, int column) {
131
132 TableCellRenderer cellRenderer = this.tableAdjust.getCellRenderer(row, column);
133 Component c = this.tableAdjust.prepareRenderer(cellRenderer, row, column);
134 return c.getPreferredSize().width + this.tableAdjust.getIntercellSpacing().width;
135 }
136
137
138
139
140 private void updateTableColumn(int column, int width) {
141 final var tableColumn = this.tableAdjust.getColumnModel().getColumn(column);
142 if (!tableColumn.getResizable()) {
143 return;
144 }
145
146 int calculatedWidth = width;
147 calculatedWidth += this.spacing;
148
149 if (this.isOnlyAdjustLarger) {
150 calculatedWidth = Math.max(calculatedWidth, tableColumn.getPreferredWidth());
151 }
152
153 this.columnSizes.put(tableColumn, tableColumn.getWidth());
154 this.tableAdjust.getTableHeader().setResizingColumn(tableColumn);
155 tableColumn.setWidth(calculatedWidth);
156 }
157
158
159
160
161 public void restoreColumns() {
162 TableColumnModel tableColumnModel = this.tableAdjust.getColumnModel();
163 for (var i = 0 ; i < tableColumnModel.getColumnCount() ; i++) {
164 this.restoreColumn(i);
165 }
166 }
167
168
169
170
171 private void restoreColumn(int column) {
172 var tableColumn = this.tableAdjust.getColumnModel().getColumn(column);
173 Integer width = this.columnSizes.get(tableColumn);
174 if (width != null) {
175 this.tableAdjust.getTableHeader().setResizingColumn(tableColumn);
176 tableColumn.setWidth(width);
177 }
178 }
179
180
181
182
183 public void setColumnHeaderIncluded(boolean isColumnHeaderIncluded) {
184 this.isColumnHeaderIncluded = isColumnHeaderIncluded;
185 }
186
187
188
189
190 public void setColumnDataIncluded(boolean isColumnDataIncluded) {
191 this.isColumnDataIncluded = isColumnDataIncluded;
192 }
193
194
195
196
197 public void setOnlyAdjustLarger(boolean isOnlyAdjustLarger) {
198 this.isOnlyAdjustLarger = isOnlyAdjustLarger;
199 }
200
201
202
203
204
205 public void setDynamicAdjustment(boolean isDynamicAdjustment) {
206
207 if (this.isDynamicAdjustment != isDynamicAdjustment) {
208 if (isDynamicAdjustment) {
209 this.tableAdjust.addPropertyChangeListener(this);
210 this.tableAdjust.getModel().addTableModelListener(this);
211 } else {
212 this.tableAdjust.removePropertyChangeListener(this);
213 this.tableAdjust.getModel().removeTableModelListener(this);
214 }
215 }
216 this.isDynamicAdjustment = isDynamicAdjustment;
217 }
218
219
220
221
222 @Override
223 public void propertyChange(PropertyChangeEvent e) {
224
225
226 if ("model".equals(e.getPropertyName())) {
227 TableModel model = (TableModel) e.getOldValue();
228 model.removeTableModelListener(this);
229
230 model = (TableModel) e.getNewValue();
231 model.addTableModelListener(this);
232 this.adjustColumns();
233 }
234 }
235
236
237
238
239 @Override
240 public void tableChanged(TableModelEvent e) {
241 if (!this.isColumnDataIncluded) {
242 return;
243 }
244
245
246 if (e.getType() == TableModelEvent.UPDATE) {
247 int column = this.tableAdjust.convertColumnIndexToView(e.getColumn());
248
249
250 if (this.isOnlyAdjustLarger) {
251 int row = e.getFirstRow();
252 var tableColumn = this.tableAdjust.getColumnModel().getColumn(column);
253 if (tableColumn.getResizable()) {
254 int width = this.getCellDataWidth(row, column);
255 this.updateTableColumn(column, width);
256 }
257 } else {
258 this.adjustColumn(column);
259 }
260 } else {
261 this.adjustColumns();
262 }
263 }
264
265
266
267
268 private void installActions() {
269 this.installColumnAction(true, true, "adjustColumn", "control ADD");
270 this.installColumnAction(false, true, "adjustColumns", "control shift ADD");
271 this.installColumnAction(true, false, "restoreColumn", "control SUBTRACT");
272 this.installColumnAction(false, false, "restoreColumns", "control shift SUBTRACT");
273
274 this.installToggleAction(true, false, "toggleDynamic", "control MULTIPLY");
275 this.installToggleAction(false, true, "toggleLarger", "control DIVIDE");
276 }
277
278
279
280
281 private void installColumnAction(boolean isSelectedColumn, boolean isAdjust, String key, String keyStroke) {
282 Action action = new ColumnAction(isSelectedColumn, isAdjust);
283 var ks = KeyStroke.getKeyStroke(keyStroke);
284
285 this.tableAdjust.getInputMap().put(ks, key);
286 this.tableAdjust.getActionMap().put(key, action);
287 }
288
289
290
291
292 private void installToggleAction(boolean isToggleDynamic, boolean isToggleLarger, String key, String keyStroke) {
293 Action action = new ToggleAction(isToggleDynamic, isToggleLarger);
294 var ks = KeyStroke.getKeyStroke(keyStroke);
295
296 this.tableAdjust.getInputMap().put(ks, key);
297 this.tableAdjust.getActionMap().put(key, action);
298 }
299
300
301
302
303 class ColumnAction extends AbstractAction {
304
305 private final boolean isSelectedColumn;
306 private final boolean isAdjust;
307
308 public ColumnAction(boolean isSelectedColumn, boolean isAdjust) {
309 this.isSelectedColumn = isSelectedColumn;
310 this.isAdjust = isAdjust;
311 }
312
313 @Override
314 public void actionPerformed(ActionEvent e) {
315
316 if (this.isSelectedColumn) {
317 int[] columns = AdjusterTableColumn.this.tableAdjust.getSelectedColumns();
318
319 for (int column: columns) {
320 if (this.isAdjust) {
321 AdjusterTableColumn.this.adjustColumn(column);
322 } else {
323 AdjusterTableColumn.this.restoreColumn(column);
324 }
325 }
326 } else {
327 if (this.isAdjust) {
328 AdjusterTableColumn.this.adjustColumns();
329 } else {
330 AdjusterTableColumn.this.restoreColumns();
331 }
332 }
333 }
334 }
335
336
337
338
339
340 class ToggleAction extends AbstractAction {
341
342 private final boolean isToggleDynamic;
343 private final boolean isToggleLarger;
344
345 public ToggleAction(boolean isToggleDynamic, boolean isToggleLarger) {
346 this.isToggleDynamic = isToggleDynamic;
347 this.isToggleLarger = isToggleLarger;
348 }
349
350 @Override
351 public void actionPerformed(ActionEvent e) {
352 if (this.isToggleDynamic) {
353 AdjusterTableColumn.this.setDynamicAdjustment(!AdjusterTableColumn.this.isDynamicAdjustment);
354 } else if (this.isToggleLarger) {
355 AdjusterTableColumn.this.setOnlyAdjustLarger(!AdjusterTableColumn.this.isOnlyAdjustLarger);
356 }
357 }
358 }
359 }