001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.ui;
030
031import java.awt.BorderLayout;
032import java.util.Calendar;
033import java.util.Date;
034import java.util.GregorianCalendar;
035import java.util.TimeZone;
036
037import javax.swing.Box;
038import javax.swing.JComponent;
039import javax.swing.JLabel;
040import javax.swing.JPanel;
041import javax.swing.JSpinner;
042import javax.swing.SpinnerDateModel;
043
044import com.toedter.calendar.JCalendar;
045import com.toedter.calendar.JDateChooser;
046
047import ucar.unidata.idv.IdvConstants;
048import ucar.unidata.util.GuiUtils;
049
050/**
051 * This class is just a backport of the IDV's old {@code DateTimePicker}.
052 */
053public class JCalendarPicker extends JPanel {
054
055    /** Date chooser */
056    private JDateChooser dateChooser;
057
058    /** SpinnerDateModel */
059    private SpinnerDateModel timeModel;
060
061    /** JCalendar */
062    private JCalendar jc;
063
064    /**
065     * Default constructor. Builds a {@code JCalendarPicker} with a {@code null}
066     * initial date and includes the {@literal "hour picker"}.
067     */
068    public JCalendarPicker() {
069        this(null, null, true);
070    }
071
072    /**
073     * Creates a {@code JCalendarPicker} with a {@code null} initial date.
074     *
075     * @param includeHours Whether or not to include an hour picker.
076     */
077    public JCalendarPicker(boolean includeHours) {
078        this(null, null, includeHours);
079    }
080
081    /**
082     * Create a {@code JCalendarPicker} with the initial date.
083     *
084     * @param date Initial date. {@code null} is allowed.
085     */
086    public JCalendarPicker(Date date) {
087        this(date, null, true);
088    }
089
090    /**
091     * Create a {@code JCalendarPicker} with the initial date.
092     *
093     * @param date Initial date. {@code null} is allowed.
094     * @param timeZoneId Time zone identifier. If {@code null},
095     * {@link IdvConstants#DEFAULT_TIMEZONE} is used.
096     * @param includeHours {@code true} to have an hour picker.
097     *
098     * @see TimeZone#getTimeZone(String)
099     */
100    public JCalendarPicker(Date date, String timeZoneId, boolean includeHours) {
101        jc = new JCalendar();
102        Calendar calendar = getCalendar(null);
103        jc.getDayChooser().setCalendar(calendar);
104        jc.setCalendar(calendar);
105        
106        dateChooser = new JDateChooser(jc, new Date(), null, new JCalendarDateEditor());
107        setLayout(new BorderLayout());
108
109        // Create a date spinner that controls the hours
110        timeModel = new SpinnerDateModel(calendar.getTime(), null, null,
111            Calendar.HOUR_OF_DAY);
112        JSpinner spinner = new JSpinner(timeModel);
113        JSpinner.DateEditor editor =
114            new JSpinner.DateEditor(spinner, "HH:mm");
115        spinner.setEditor(editor);
116        JComponent timeComp;
117        if (timeZoneId == null) {
118            timeZoneId = IdvConstants.DEFAULT_TIMEZONE;
119        }
120        String timeZoneLabel = ' ' + TimeZone.getTimeZone(timeZoneId).getID();
121        if (includeHours) {
122            timeComp = GuiUtils.hbox(spinner, new JLabel(timeZoneLabel), 5);
123        } else {
124            timeComp = new JLabel(timeZoneLabel);
125        }
126        add(BorderLayout.CENTER, GuiUtils.hbox(dateChooser, 
127                GuiUtils.hbox(timeComp, Box.createHorizontalStrut(8))));
128        if (date != null) {
129            setDate(date);
130        }
131    }
132
133    public JDateChooser getDateChooser() {
134        return dateChooser;
135    }
136
137    /**
138     * Get the {@link Date} that has been set.
139     *
140     * @return {@code Date} that represents the user's selection.
141     */
142    public Date getDate() {
143        Date d = dateChooser.getDate();
144        Calendar c0 = getCalendar(d);
145        Calendar c1 = new GregorianCalendar(c0.get(Calendar.YEAR),
146            c0.get(Calendar.MONTH),
147            c0.get(Calendar.DAY_OF_MONTH));
148
149        if (timeModel != null) {
150            Date time = timeModel.getDate();
151            Calendar timeCal = getCalendar(time);
152            c1.set(Calendar.HOUR_OF_DAY, timeCal.get(Calendar.HOUR_OF_DAY));
153            c1.set(Calendar.MINUTE, timeCal.get(Calendar.MINUTE));
154            c1.set(Calendar.SECOND, timeCal.get(Calendar.SECOND));
155        }
156        return c1.getTime();
157    }
158
159    /**
160     * Get the calendar for this instance.
161     *
162     * @param date The date. If {@code null}, the current time in the system's
163     * default time zone will be used instead.
164     *
165     * @return {@link GregorianCalendar} associated with {@code date}.
166     */
167    private Calendar getCalendar(Date date) {
168        Calendar calendar = new GregorianCalendar();
169        if (date != null) {
170            calendar.setTime(date);
171        }
172        return calendar;
173    }
174
175    /**
176     * Set the {@link Date} that will be used to populate the GUI components.
177     *
178     * @param date {@code Date}.
179     */
180    public void setDate(Date date) {
181        Calendar c = getCalendar(date);
182        dateChooser.setDate(c.getTime());
183        timeModel.setValue(date);
184    }
185
186    /**
187     * Get the user's selection as a {@literal "YYYY-MM-DD"} string.
188     * <b>This method ignores time zones.</b>
189     *
190     * @return Selected date as a {@literal "YYYY-MM-DD"} string.
191     */
192    public String getUserSelectedDay() {
193        Calendar selectedDay = getCalendar(dateChooser.getDate());
194        int year = selectedDay.get(Calendar.YEAR);
195        int month = selectedDay.get(Calendar.MONTH) + 1; // zero-based
196        int day = selectedDay.get(Calendar.DAY_OF_MONTH);
197        return String.format("%d-%02d-%02d", year, month, day);
198    }
199}