001package arcommon.ui.window;
002
003// Code copied from https://examples.javacodegeeks.com/desktop-java/swing/java-swing-date-picker-example
004// and modified a lot
005
006import java.awt.BorderLayout;
007import java.awt.Color;
008import java.awt.Dimension;
009import java.awt.GridLayout;
010import java.awt.Point;
011import java.awt.event.ActionEvent;
012import java.awt.event.ActionListener;
013import java.text.ParseException;
014import java.text.SimpleDateFormat;
015import java.util.Calendar;
016import java.util.Date;
017
018import javax.swing.JButton;
019import javax.swing.JComponent;
020import javax.swing.JLabel;
021import javax.swing.JPanel;
022import javax.swing.SwingUtilities;
023
024import arcommon.DateUtil;
025import arcommon.Util;
026import arcommon.ui.other.GUIUtil;
027
028public class DatePickerJDialog extends AbstractJDialog {
029    private static final long serialVersionUID = 2861477430791190912L;
030
031    private static final SimpleDateFormat DEFAULT_DATE_FORMAT = DateUtil.DATE_FORMAT;
032    
033    private static final String[] HEADER = {"Mon", "Tue", "Wed", "Thur", "Fri", "Sat", "Sun"};
034    private static final int ROWS = 7;
035    private static final int COLUMNS = 7;
036
037    private SimpleDateFormat simpleDateFormat;
038    
039    private int month;
040    private int year;
041    
042    private JLabel monthYearJLabel;
043    private String pickedDay;
044    private int pickedYear;
045    private boolean onlyYearWasSelected;
046    private JButton[] buttons;
047
048    private int nowYear;
049    private int nowMonth;
050    private int nowDay;
051
052    private int startYear;
053    private int startMonth;
054    private int startDay;
055
056    public DatePickerJDialog(String title, JComponent field, String dateString) {
057        this(title, field, dateString, null);
058    }
059    
060    public DatePickerJDialog(String title, JComponent field, String dateString, boolean allowSelectYearOnly, JComponent extraComponent) {
061        this(title, field, dateString, null, allowSelectYearOnly, extraComponent);
062    }
063    
064    public DatePickerJDialog(String title, JComponent field, String dateString, SimpleDateFormat psimpleDateFormat) {
065        this(title, field, dateString, null, false, null);
066    }
067    
068    public DatePickerJDialog(String title, JComponent field, String dateString, SimpleDateFormat psimpleDateFormat, boolean allowSelectYearOnly, JComponent extraComponent) {
069        super(title != null ? title : "Select a date");
070        
071        if (psimpleDateFormat != null) {
072            simpleDateFormat = psimpleDateFormat;
073        } else {
074            simpleDateFormat = DEFAULT_DATE_FORMAT;
075        }
076        
077        Calendar calendar = Calendar.getInstance();
078        nowYear = calendar.get(Calendar.YEAR);
079        nowMonth = calendar.get(Calendar.MONTH);
080        nowDay = calendar.get(Calendar.DAY_OF_MONTH);
081        boolean yearOnly = false;
082        if (dateString != null && dateString.length() > 0) {
083            try {
084                Date date = DateUtil.stringToDate(simpleDateFormat, dateString, true);
085                calendar.setTime(date);
086            } catch (ParseException e) {
087                if (allowSelectYearOnly) {
088                    try {
089                        Date date = DateUtil.stringToDate(DateUtil.DATE_FORMAT_YEAR_ONLY, dateString, true);
090                        calendar.setTime(date);
091                        yearOnly = true;
092                    } catch (ParseException e2) {
093                        Util.printMessageWithSourceLine(e2.getMessage());
094                    }
095                } else {
096                    Util.printMessageWithSourceLine(e.getMessage());
097                }
098            }
099        }
100        year = calendar.get(Calendar.YEAR);
101        month = calendar.get(Calendar.MONTH);
102        
103        startYear = calendar.get(Calendar.YEAR);
104        startMonth = calendar.get(Calendar.MONTH);
105        startDay = calendar.get(Calendar.DAY_OF_MONTH);
106        
107        if (yearOnly) {
108            calendar = Calendar.getInstance();
109            month = calendar.get(Calendar.MONTH);
110            startMonth = calendar.get(Calendar.MONTH);
111            startDay = -1;
112        }
113        
114        JPanel northPanel = new JPanel(new GridLayout(1, 3));
115        add(northPanel, BorderLayout.NORTH);
116        
117        JPanel previousPanel = new JPanel(new GridLayout(1, 2));
118        northPanel.add(previousPanel);
119
120        JButton previousYear = new JButton("<<");
121        previousPanel.add(previousYear);
122        previousYear.addActionListener(new ActionListener() {
123            @Override
124            public void actionPerformed(ActionEvent event) {
125                year--;
126                updateDate();
127            }
128        });
129
130        JButton previousMonth = new JButton("<");
131        previousPanel.add(previousMonth);
132        previousMonth.addActionListener(new ActionListener() {
133            @Override
134            public void actionPerformed(ActionEvent event) {
135                month--;
136                updateDate();
137            }
138        });
139        
140        monthYearJLabel = new JLabel("", JLabel.CENTER);
141        northPanel.add(monthYearJLabel);
142        
143        JPanel nextPanel = new JPanel(new GridLayout(1, 2));
144        northPanel.add(nextPanel);
145
146        JButton nextMonth = new JButton(">");
147        nextPanel.add(nextMonth);
148        nextMonth.addActionListener(new ActionListener() {
149            public void actionPerformed(ActionEvent ae) {
150                month++;
151                updateDate();
152            }
153        });
154
155        JButton nextYear = new JButton(">>");
156        nextPanel.add(nextYear);
157        nextYear.addActionListener(new ActionListener() {
158            public void actionPerformed(ActionEvent ae) {
159                year++;
160                updateDate();
161            }
162        });
163        
164        JPanel centerPanel = new JPanel(new GridLayout(ROWS, COLUMNS));
165        add(centerPanel, BorderLayout.CENTER);
166        centerPanel.setPreferredSize(new Dimension(400, 120));
167
168        for (int x = 0; x < COLUMNS; x++) {
169            JLabel label = new JLabel(HEADER[x], JLabel.CENTER);
170            centerPanel.add(label);
171        }
172        
173        buttons = new JButton[(ROWS - 1) * COLUMNS];
174        for (int i = 0; i < buttons.length; i++) {
175            buttons[i] = new JButton();
176            centerPanel.add(buttons[i]);
177            final int finalI = i;
178            buttons[i].addActionListener(new ActionListener() {
179                public void actionPerformed(ActionEvent ae) {
180                    String text = buttons[finalI].getActionCommand();
181                    if (text.length() == 0) {
182                        return ;
183                    }
184                    
185                    pickedDay = text;
186                    dispose();
187                }
188            });
189        }
190        
191        JPanel southPanel = new JPanel(new GridLayout(1, 3));
192        add(southPanel, BorderLayout.SOUTH);
193        
194        if (extraComponent != null) {
195            southPanel.add(extraComponent);
196        } else {
197            southPanel.add(new JPanel());
198        }
199
200        if (allowSelectYearOnly) {
201            JButton selectYearButton = new JButton("Select year");
202            southPanel.add(selectYearButton);
203            selectYearButton.addActionListener(new ActionListener() {
204                public void actionPerformed(ActionEvent ae) {
205                    pickedYear = year;
206                    onlyYearWasSelected = true;
207                    dispose();
208                }
209            });
210        } else {
211            southPanel.add(new JPanel());
212        }
213
214        JButton cancelButton = new JButton("Cancel");
215        southPanel.add(cancelButton);
216        cancelButton.addActionListener(new ActionListener() {
217            public void actionPerformed(ActionEvent ae) {
218                pickedDay = null;
219                dispose();
220            }
221        });
222        
223        // This is done invoked later to make buttons[x].requestFocusInWindow() work.
224        SwingUtilities.invokeLater(new Runnable() {
225            @Override
226            public void run() {
227                updateDate();
228            }
229        });
230        
231        pack();
232        
233        Point locationOnScreen = field.getLocationOnScreen();
234        int x = locationOnScreen.x + field.getWidth() / 2 - getWidth() / 2;
235        int y = locationOnScreen.y + field.getHeight();
236        
237        Dimension screenSize = GUIUtil.getScreenSize();
238        if (x + getWidth() > screenSize.width) {
239            System.out.println("x + width > screenSize.width " + this);
240            x = screenSize.width - getWidth() - 50;
241        }
242        if (y + getHeight() > screenSize.height) {
243            System.out.println("y + height > screenSize.height " + this);
244            y = screenSize.height - getHeight() - 50;
245        }
246        if (x < 0) {
247            x = 50;
248        }
249        if (y < 0) {
250            y = 50;
251        }
252        
253        setLocation(x, y);
254        setModal(true);
255        setVisible(true);
256    }
257
258    public void updateDate() {
259        for (int x = 0; x < buttons.length; x++) {
260            buttons[x].setText("");
261            buttons[x].setEnabled(false);
262            buttons[x].setBackground(new Color(215, 215, 215));
263            buttons[x].setForeground(Color.BLACK);
264        }
265        
266        Calendar calendar = Calendar.getInstance();
267        calendar.set(year, month, 1);
268        int viewedYear = calendar.get(Calendar.YEAR);
269        int viewedMonth = calendar.get(Calendar.MONTH);
270        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
271        int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
272        for (int x = (dayOfWeek + 5) % COLUMNS, day = 1; day <= daysInMonth; x++, day++) {
273            buttons[x].setText("" + day);
274            buttons[x].setEnabled(true);
275            if (viewedYear == nowYear && viewedMonth == nowMonth && day == nowDay) {
276                buttons[x].setForeground(Color.RED);
277                if (viewedYear != startYear || viewedMonth != startMonth) {
278                    buttons[x].requestFocusInWindow();
279                }
280            }
281            if (viewedYear == startYear && viewedMonth == startMonth && day == startDay) {
282                buttons[x].setBackground(new Color(150, 150, 255));
283                buttons[x].requestFocusInWindow();
284            }
285        }
286
287        SimpleDateFormat sdf = new SimpleDateFormat("MMMM yyyy");
288        monthYearJLabel.setText(sdf.format(calendar.getTime()));
289    }
290    
291    public boolean onlyYearWasSelected() {
292        return onlyYearWasSelected;
293    }
294
295    public int getPickedYear() {
296        return pickedYear;
297    }
298    
299    public String getPickedDate() {
300        if (pickedDay == null || pickedDay.equals("")) {
301            return null;
302        }
303        
304        if (onlyYearWasSelected) {
305            throw new RuntimeException("onlyYearWasSelected use getPickedYear() and onlyYearWasSelected()");
306        }
307
308        Calendar calendar = Calendar.getInstance();
309        calendar.set(year, month, Integer.parseInt(pickedDay));
310        return simpleDateFormat.format(calendar.getTime());
311    }
312}