-
Notifications
You must be signed in to change notification settings - Fork 91
/
TimeTableEditor.java
283 lines (263 loc) · 11.8 KB
/
TimeTableEditor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
* The MIT License
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.lgooddatepicker.tableeditors;
import com.github.lgooddatepicker.components.TimePicker;
import com.github.lgooddatepicker.components.TimePickerSettings;
import com.github.lgooddatepicker.zinternaltools.InternalUtilities;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.time.LocalTime;
import java.util.EventObject;
import javax.swing.AbstractCellEditor;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
/**
* TimeTableEditor, This class is used to add a TimePicker to cells (or entire columns) of a Swing
* JTable or a SwingX JXTable component. This class can be used as a table cell "Editor", or as a
* "Renderer", or as both editor and renderer. Separate class instances should be used for the
* editor and the renderer. <code>
* // Usage example:
* // Create a table.
* JTable table = new JTable();
* //Set up a renderer and editor for the LocalTime type.
* table.setDefaultRenderer(LocalTime.class, new TimeTableEditor());
* table.setDefaultEditor(LocalTime.class, new TimeTableEditor());
* </code> The TimePicker and TimePickerSettings: The time picker and picker settings can be
* accessed with the supplied getter functions. Note that most of the settings for the time picker
* are left at their default values, but a few (mostly cosmetic) settings are changed in the
* TimeTableEditor constructor. See the TimeTableEditor constructor implementation to learn which
* default settings were changed.
*
* <p>Auto adjust row height: By default, this class will automatically adjust the minimum height of
* all table rows. This occurs during the first time that any cell with an editor or render is
* displayed. The row height is only adjusted if the current table row height value is below the
* minimum value that is needed to display the time picker component. This auto adjust behavior can
* be turned off from the TimeTableEditor constructor.
*/
public class TimeTableEditor extends AbstractCellEditor
implements TableCellEditor, TableCellRenderer {
/**
* autoAdjustMinimumTableRowHeight, This indicates whether the minimum table row height (for all
* rows) should be modified when an editor or render is displayed. The row height is only adjusted
* if it is below the minimum value needed to display the time picker component.
*/
private boolean autoAdjustMinimumTableRowHeight = true;
/**
* clickCountToEdit, An integer specifying the number of clicks needed to start editing. Even if
* clickCountToEdit is defined as zero, it will not initiate until a click occurs.
*/
public int clickCountToEdit = 1;
/**
* matchTableBackgroundColor, This indicates whether this table editor should set the picker text
* area background color to match the background color of the table. The default value is true.
*/
private boolean matchTableBackgroundColor = true;
/**
* matchTableSelectionBackgroundColor, This indicates whether this table editor should set the
* picker text area background color to match the background color of the table selection (when
* selected). The default value is true.
*/
private boolean matchTableSelectionBackgroundColor = true;
/** borderFocusedCell, This holds the border that is used when a cell has focus. */
private Border borderFocusedCell;
/** borderUnfocusedCell, This holds the border that is used when a cell does not have focus. */
private Border borderUnfocusedCell;
/** timepicker, This holds the time picker instance. */
private TimePicker timePicker;
/** minimumRowHeight, This holds the minimum row height needed to display the time picker. */
private int minimumRowHeightInPixels;
/** Constructor, default. */
public TimeTableEditor() {
this(true, true, true);
}
/**
* Constructor, with options.
*
* @param autoAdjustMinimumTableRowHeight Set this to true to have this class automatically adjust
* the the minimum row height of all rows in the table, the first time that a TimeTableEditor
* is displayed. Set this to false to turn off any row height adjustments. The default value
* is true.
*/
public TimeTableEditor(
boolean autoAdjustMinimumTableRowHeight,
boolean matchTableBackgroundColor,
boolean matchTableSelectionBackgroundColor) {
// Save the constructor parameters.
this.autoAdjustMinimumTableRowHeight = autoAdjustMinimumTableRowHeight;
this.matchTableBackgroundColor = matchTableBackgroundColor;
this.matchTableSelectionBackgroundColor = matchTableSelectionBackgroundColor;
// Create the borders that should be used for focused and unfocused cells.
JLabel exampleDefaultRenderer =
(JLabel)
new DefaultTableCellRenderer()
.getTableCellRendererComponent(new JTable(), "", true, true, 0, 0);
borderFocusedCell = exampleDefaultRenderer.getBorder();
borderUnfocusedCell = new EmptyBorder(1, 1, 1, 1);
// Create and set up the time picker.
timePicker = new TimePicker();
timePicker.setEnableArrowKeys(false);
timePicker.setBorder(borderUnfocusedCell);
timePicker.getComponentTimeTextField().setBorder(null);
// Adjust any needed time picker settings.
TimePickerSettings settings = timePicker.getSettings();
settings.setGapBeforeButtonPixels(0);
settings.setSizeTextFieldMinimumWidthDefaultOverride(false);
settings.setSizeTextFieldMinimumWidth(20);
// Calculate and store the minimum row height needed to display the time picker.
minimumRowHeightInPixels = (timePicker.getPreferredSize().height + 1);
}
/**
* getCellEditorValue, This returns the value contained in the editor. This is required by the
* CellEditor interface.
*/
@Override
public Object getCellEditorValue() {
return timePicker.getTime();
}
/** getTimePicker, Returns the TimePicker which is used by this class. */
public TimePicker getTimePicker() {
return timePicker;
}
/**
* getTimePickerSettings, Returns the TimePickerSettings for the TimePicker being used by this
* class. These settings can be adjusted as desired by the programmer.
*/
public TimePickerSettings getTimePickerSettings() {
return timePicker.getSettings();
}
/**
* getTableCellEditorComponent, this returns the editor that is used for editing the cell. This is
* required by the TableCellEditor interface.
*
* <p>For additional details, see the Javadocs for the function:
* TableCellEditor.getTableCellEditorComponent().
*/
@Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
// Save the supplied value to the time picker.
setCellEditorValue(value);
// If needed, adjust the minimum row height for the table.
zAdjustTableRowHeightIfNeeded(table);
// This fixes a bug where the time text could "move around" during a table resize event.
timePicker.getComponentTimeTextField().setScrollOffset(0);
// Return the time picker component.
return timePicker;
}
/**
* getTableCellRendererComponent, Returns the renderer that is used for drawing the cell. This is
* required by the TableCellRenderer interface.
*
* <p>For additional details, see the Javadocs for the function:
* TableCellRenderer.getTableCellRendererComponent().
*/
@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
// Save the supplied value to the time picker.
setCellEditorValue(value);
// Draw the appropriate background colors to indicate a selected or unselected state.
if (isSelected) {
if (matchTableSelectionBackgroundColor) {
timePicker.getComponentTimeTextField().setBackground(table.getSelectionBackground());
timePicker.setBackground(table.getSelectionBackground());
} else {
timePicker.zDrawTextFieldIndicators();
}
}
if (!isSelected) {
if (matchTableBackgroundColor) {
timePicker.getComponentTimeTextField().setBackground(table.getBackground());
timePicker.setBackground(table.getBackground());
} else {
timePicker.zDrawTextFieldIndicators();
}
}
// Draw the appropriate borders to indicate a focused or unfocused state.
if (hasFocus) {
timePicker.setBorder(borderFocusedCell);
} else {
timePicker.setBorder(borderUnfocusedCell);
}
// If needed, adjust the minimum row height for the table.
zAdjustTableRowHeightIfNeeded(table);
// This fixes a bug where the time text could "move around" during a table resize event.
timePicker.getComponentTimeTextField().setScrollOffset(0);
// Return the time picker component.
return timePicker;
}
/**
* isCellEditable, Returns true if anEvent is not a MouseEvent. Otherwise, it returns true if the
* necessary number of clicks have occurred, and returns false otherwise.
*/
@Override
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
return ((MouseEvent) anEvent).getClickCount() >= clickCountToEdit;
}
return true;
}
/**
* setCellEditorValue, This sets the picker to an appropriate value for the supplied object. If
* the value is null, then picker will be cleared. If the value is a LocalTime instance, then the
* picker will be set to that value. All other types (including strings) will be read or converted
* to a string with a maximum length of the first 100 characters. The picker text will be set with
* the resulting string.
*/
public void setCellEditorValue(Object value) {
timePicker.clear();
if (value == null) {
return;
}
if (value instanceof LocalTime) {
LocalTime nativeValue = (LocalTime) value;
timePicker.setTime(nativeValue);
} else {
String text = value.toString();
String shorterText = InternalUtilities.safeSubstring(text, 0, 100);
timePicker.setText(shorterText);
}
}
/**
* zAdjustTableRowHeightIfNeeded, If needed, this will adjust the row height for all rows in the
* supplied table to fit the minimum row height that is needed to display the time picker
* component.
*
* <p>If autoAdjustMinimumTableRowHeight is false, this will do nothing. If the row height of the
* table is already greater than or equal to "minimumRowHeightInPixels", then this will do
* nothing.
*/
private void zAdjustTableRowHeightIfNeeded(JTable table) {
if (!autoAdjustMinimumTableRowHeight) {
return;
}
if ((table.getRowHeight() < minimumRowHeightInPixels)) {
table.setRowHeight(minimumRowHeightInPixels);
}
}
}