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 package org.jdatepicker;
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 import org.jdatepicker.constraints.DateSelectionConstraint;
55
56 import javax.swing.*;
57 import java.awt.*;
58 import java.awt.event.*;
59 import java.beans.PropertyChangeEvent;
60 import java.beans.PropertyChangeListener;
61 import java.text.DateFormat;
62 import java.text.SimpleDateFormat;
63 import java.util.Date;
64 import java.util.HashSet;
65 import java.util.Set;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class JDatePicker extends JComponent {
83
84 private static final long serialVersionUID = 2814777654384974503L;
85
86
87 public static final String COMMIT_DATE = JDatePanel.COMMIT_DATE;
88
89 private Popup popup;
90 private final FittingPopupFactory popupFactory = new FittingPopupFactory();
91 private final JFormattedTextField formattedTextField;
92 private DateComponentFormatter dateComponentFormatter;
93 private final JButton button;
94
95 private final JDatePanel datePanel;
96
97 private final Set<ActionListener> actionListeners;
98
99
100
101
102 public JDatePicker() {
103 this(new JDatePanel());
104 }
105
106
107
108
109
110
111 public JDatePicker(DateModel model) {
112 this(new JDatePanel(model));
113 }
114
115
116
117
118
119
120
121 private JDatePicker(JDatePanel datePanel) {
122 this.datePanel = datePanel;
123 datePanel.setBorder(BorderFactory.createLineBorder(datePanel.getColors().getColor(ComponentColorDefaults.Key.POPUP_BORDER)));
124
125
126 actionListeners = new HashSet<>();
127
128
129 setLayout(new BorderLayout());
130
131
132 formattedTextField = createTextField();
133 add(formattedTextField, BorderLayout.CENTER);
134
135
136 button = createButton();
137 add(button, BorderLayout.LINE_END);
138
139
140 InternalEventHandler internalEventHandler = new InternalEventHandler();
141 addHierarchyBoundsListener(internalEventHandler);
142
143 button.addActionListener(internalEventHandler);
144 formattedTextField.addPropertyChangeListener("value", internalEventHandler);
145 datePanel.addActionListener(internalEventHandler);
146 datePanel.getModel().addPropertyChangeListener(internalEventHandler);
147 long eventMask = MouseEvent.MOUSE_PRESSED;
148 Toolkit.getDefaultToolkit().addAWTEventListener(internalEventHandler, eventMask);
149 }
150
151
152
153
154
155
156 protected JFormattedTextField createTextField() {
157 dateComponentFormatter = new DateComponentFormatter();
158 JFormattedTextField textField = new JFormattedTextField(dateComponentFormatter);
159 textField.setValue(datePanel.getModel().getDate());
160 textField.setEditable(false);
161 return textField;
162 }
163
164
165
166
167
168
169 protected JButton createButton() {
170 JButton b = new JButton();
171 b.setRolloverEnabled(true);
172 b.setFocusable(false);
173 Icon icon = ComponentIconDefaults.getInstance().getPopupButtonIcon();
174 b.setIcon(icon);
175 b.setText(icon == null ? "..." : "");
176 return b;
177 }
178
179
180
181
182
183
184 public Date getDate() {
185 return getModel().getDate();
186 }
187
188
189
190
191
192
193 public void setDate(Date date) {
194 getModel().setDate(date);
195 }
196
197
198
199
200
201
202 public void addActionListener(ActionListener actionListener) {
203 actionListeners.add(actionListener);
204 datePanel.addActionListener(actionListener);
205 }
206
207
208
209
210
211
212 public void removeActionListener(ActionListener actionListener) {
213 actionListeners.remove(actionListener);
214 datePanel.removeActionListener(actionListener);
215 }
216
217
218
219
220
221
222 public DateModel getModel() {
223 return datePanel.getModel();
224 }
225
226
227
228
229
230
231 public JFormattedTextField getEditor() {
232 return formattedTextField;
233 }
234
235
236
237
238
239
240 public void setTextEditable(boolean editable) {
241 formattedTextField.setEditable(editable);
242 }
243
244
245
246
247
248
249 public boolean isTextEditable() {
250 return formattedTextField.isEditable();
251 }
252
253
254
255
256
257
258 public void setButtonFocusable(boolean focusable) {
259 button.setFocusable(focusable);
260 }
261
262
263
264
265
266
267 public boolean getButtonFocusable() {
268 return button.isFocusable();
269 }
270
271
272
273
274 private void showPopup() {
275 if (popup == null) {
276 datePanel.setVisible(true);
277 Point loc = getLocationOnScreen();
278 popup = popupFactory.getPopup(this, datePanel, loc.x, loc.y + getHeight());
279 SwingUtilities.invokeLater(new Runnable() {
280 @Override
281 public void run() {
282 popup.show();
283 }
284 });
285 }
286 }
287
288
289
290
291 private void hidePopup() {
292 if (popup != null) {
293 popup.hide();
294 popup = null;
295 }
296 }
297
298 private Set<Component> getAllComponents(Component component) {
299 Set<Component> children = new HashSet<>();
300 children.add(component);
301 if (component instanceof Container) {
302 Container container = (Container) component;
303 Component[] components = container.getComponents();
304 for (Component component1 : components) {
305 children.addAll(getAllComponents(component1));
306 }
307 }
308 return children;
309 }
310
311
312
313
314
315
316 public boolean isDoubleClickAction() {
317 return datePanel.isDoubleClickAction();
318 }
319
320
321
322
323
324
325 public boolean isShowYearButtons() {
326 return datePanel.isShowYearButtons();
327 }
328
329
330
331
332
333
334 public void setDoubleClickAction(boolean doubleClickAction) {
335 datePanel.setDoubleClickAction(doubleClickAction);
336 }
337
338
339
340
341
342
343 public void setShowYearButtons(boolean showYearButtons) {
344 datePanel.setShowYearButtons(showYearButtons);
345 }
346
347
348
349
350
351
352 public void addDateSelectionConstraint(DateSelectionConstraint constraint) {
353 datePanel.addDateSelectionConstraint(constraint);
354 }
355
356
357
358
359
360
361 public void removeDateSelectionConstraint(DateSelectionConstraint constraint) {
362 datePanel.removeDateSelectionConstraint(constraint);
363 }
364
365
366
367
368 public void removeAllDateSelectionConstraints() {
369 datePanel.removeAllDateSelectionConstraints();
370 }
371
372
373
374
375
376
377 public Set<DateSelectionConstraint> getDateSelectionConstraints() {
378 return datePanel.getDateSelectionConstraints();
379 }
380
381
382 @Override
383 public void setVisible(boolean aFlag) {
384 if (!aFlag) {
385 hidePopup();
386 }
387 super.setVisible(aFlag);
388 }
389
390
391 @Override
392 public void setEnabled(boolean enabled) {
393 button.setEnabled(enabled);
394 datePanel.setEnabled(enabled);
395 formattedTextField.setEnabled(enabled);
396
397 super.setEnabled(enabled);
398 }
399
400
401
402
403
404
405
406 public void setDateFormat(String datePattern, Date default2DigitYearStart) {
407
408 SimpleDateFormat outputDateFormat = new SimpleDateFormat(datePattern);
409
410 SimpleDateFormat inputDateFormat;
411 if (datePattern.contains("yyyy")) {
412 inputDateFormat = new SimpleDateFormat(datePattern.replace("yyyy", "yy"));
413 } else {
414 inputDateFormat = outputDateFormat;
415 }
416 inputDateFormat.set2DigitYearStart(default2DigitYearStart);
417
418 setFormat(AbstractComponentFormat.Key.OUTPUT_DATE_FIELD, outputDateFormat);
419 setFormat(AbstractComponentFormat.Key.INPUT_DATE_FIELD, inputDateFormat);
420 }
421
422
423
424
425
426
427
428 public void setFormat(AbstractComponentFormat.Key formatKey, DateFormat format) {
429 dateComponentFormatter.setFormat(formatKey, format);
430 }
431
432
433
434
435
436
437
438 public void setColor(AbstractComponentColor.Key colorKey, Color color) {
439 datePanel.getColors().setColor(colorKey, color);
440 }
441
442 private void fireActionPerformed() {
443 for (ActionListener actionListener : actionListeners) {
444 actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, COMMIT_DATE));
445 }
446 }
447
448
449
450
451 private class InternalEventHandler implements ActionListener, HierarchyBoundsListener, PropertyChangeListener, AWTEventListener {
452
453 private boolean modelAdjusting;
454
455 public void ancestorMoved(HierarchyEvent e) {
456 hidePopup();
457 }
458
459 public void ancestorResized(HierarchyEvent e) {
460 hidePopup();
461 }
462
463 public void actionPerformed(ActionEvent e) {
464 if (e.getSource() == button) {
465 if (popup == null) {
466 showPopup();
467 } else {
468 hidePopup();
469 }
470 } else if (e.getSource() == datePanel) {
471 hidePopup();
472 }
473 }
474
475 public void propertyChange(PropertyChangeEvent evt) {
476
477 if (modelAdjusting) {
478 return;
479 }
480
481 try {
482 modelAdjusting = true;
483
484 if (evt.getSource() == formattedTextField) {
485
486 if (evt.getOldValue() == null && evt.getNewValue() == null) {
487 return;
488 }
489 if (evt.getOldValue() != null && evt.getOldValue().equals(evt.getNewValue())) {
490 return;
491 }
492 if (!formattedTextField.isEditable()) {
493 return;
494 }
495
496
497 if (evt.getNewValue() instanceof Date) {
498 Date value = (Date) evt.getNewValue();
499 DateModel tempModel = new DateModel(value);
500
501 if (!datePanel.checkConstraints(tempModel)) {
502
503 formattedTextField.setValue(evt.getOldValue());
504 return;
505 }
506 getModel().setDate(tempModel.getDate());
507
508 } else {
509
510
511 getModel().setDate(null);
512 }
513
514
515 fireActionPerformed();
516
517 } else if (evt.getSource() == getModel()) {
518
519 if (DateModel.PROPERTY_VALUE.equals(evt.getPropertyName())) {
520 formattedTextField.setValue(getModel().getDate());
521 }
522
523 }
524
525 } finally {
526 modelAdjusting = false;
527 }
528 }
529
530 public void eventDispatched(AWTEvent event) {
531 if (MouseEvent.MOUSE_CLICKED == event.getID() && event.getSource() != button) {
532 Set<Component> components = getAllComponents(datePanel);
533 boolean clickInPopup = false;
534 for (Component component : components) {
535 if (event.getSource() == component) {
536 clickInPopup = true;
537 }
538 }
539 if (!clickInPopup) {
540 hidePopup();
541 }
542 }
543 }
544
545 }
546
547 }