de.bund.bfr.knime.pmm.common/src/de/bund/bfr/knime/pmm/common/ui/DateInputDialog.java
/*
* ------------------------------------------------------------------------
* Copyright by KNIME GmbH, Konstanz, Germany
* Website: http://www.knime.org; Email: contact@knime.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
* Additional permission under GNU GPL version 3 section 7:
*
* KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs.
* Hence, KNIME and ECLIPSE are both independent programs and are not
* derived from each other. Should, however, the interpretation of the
* GNU GPL Version 3 ("License") under any applicable laws result in
* KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants
* you the additional permission to use and propagate KNIME together with
* ECLIPSE with only the license terms in place for ECLIPSE applying to
* ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the
* license terms of ECLIPSE themselves allow for the respective use and
* propagation of ECLIPSE together with KNIME.
*
* Additional permission relating to nodes for KNIME that extend the Node
* Extension (and in particular that are based on subclasses of NodeModel,
* NodeDialog, and NodeView) and that only interoperate with KNIME through
* standard APIs ("Nodes"):
* Nodes are deemed to be separate and independent programs and to not be
* covered works. Notwithstanding anything to the contrary in the
* License, the License does not apply to Nodes, you are not required to
* license Nodes under the License, and you are granted a license to
* prepare and propagate Nodes, in each case even if such Nodes are
* propagated with or for interoperation with KNIME. The owner of a Node
* may freely choose the license terms applicable to such Node, including
* when such Node is propagated with or for interoperation with KNIME.
* ---------------------------------------------------------------------
*
* Created on 20.03.2013 by peter
*/
package de.bund.bfr.knime.pmm.common.ui;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import com.toedter.calendar.JDateChooser;
/**
* Version of {@link org.knime.core.node.util.DateInputDialog} with temporary
* fix for the JDateChooser until the next KNIME with this fix is released.
*/
public class DateInputDialog extends JPanel {
public static enum Mode {
NODATE, NOTIME, HOURS, MINUTES, SECONDS
};
/**
*
*/
private static final long serialVersionUID = 1064690706141780973L;
/**
* Constant used for Horizontal gaps.
*/
private static final int HORIZ_SPACE = 10;
/**
* Constant used for vertical gaps.
*/
private static final int VERT_SPACE = 10;
/**
* Constant used for the dialog width.
*/
private static final int PANEL_WIDTH = 400;
/**
* Constant used for the spinner fields width.
*/
private static final int SPINNER_WIDTH = 10;
private JDateChooser m_startDate;
private JCheckBox m_useHours;
private JCheckBox m_useMinutes;
private JCheckBox m_useSeconds;
private JSpinner m_hours;
private JSpinner m_minutes;
private JSpinner m_seconds;
private boolean m_displayHours = false;
private boolean m_displayMinutes = false;
private boolean m_displaySeconds = false;
private boolean m_isOptional = true;
private Mode m_usedMode;
private JCheckBox m_useDate;
/**
*
* Returns how the dialog is configured, meaning which fields are displayed
* in it.
*
*
* @return Mode enum, describing the visible fields of the dialog.
*/
public Mode getIntitalMode() {
return m_usedMode;
}
@Override
public void setEnabled(final boolean enabled) {
if (enabled) {
m_startDate.setEnabled(true);
m_useMinutes.setEnabled(true);
} else {
m_startDate.setEnabled(false);
m_useHours.setEnabled(false);
m_useMinutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_hours.setEnabled(false);
m_minutes.setEnabled(false);
m_seconds.setEnabled(false);
}
}
/**
* This method should be called during the loading of the Dialog, if the
* last used date should be displayed again. Therefore the date is needed in
* the timesInMillies format additionally the Mode (the status of the
* selected time fields) is needed.
*
* @param date
* Time in milliseconds
* @param mode
* Enum to specify which fields of the dialog are enabled and
* selected
*/
public void setDateAndMode(final long date, final Mode mode) {
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(date);
setDateAndMode(cal.getTime(), mode);
}
/**
*
* @param date
* Date to display in the Selection
* @param mode
* Depending on the Mode during instantiation, sets the possible
* fields
*/
public void setDateAndMode(final Date date, final Mode mode) {
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setTime(date);
int hours = cal.get(Calendar.HOUR_OF_DAY);
int minutes = cal.get(Calendar.MINUTE);
int seconds = cal.get(Calendar.SECOND);
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
m_startDate.setDate(cal.getTime());
switch (mode) {
case NODATE:
m_useDate.setEnabled(true);
if (m_isOptional) {
m_useDate.setSelected(false);
m_startDate.setEnabled(false);
m_useHours.setEnabled(false);
} else {
m_useDate.setSelected(true);
m_startDate.setEnabled(true);
m_useHours.setEnabled(true);
}
m_useMinutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_hours.setEnabled(false);
m_hours.setValue(0);
m_minutes.setEnabled(false);
m_minutes.setValue(0);
m_seconds.setEnabled(false);
m_seconds.setValue(0);
break;
case HOURS:
m_useDate.setSelected(true);
m_useDate.setEnabled(true);
m_startDate.setEnabled(true);
m_displayHours = true;
m_hours.setValue(hours);
m_seconds.setEnabled(false);
m_useSeconds.setEnabled(false);
m_minutes.setEnabled(false);
m_useMinutes.setEnabled(true);
m_hours.setEnabled(true);
m_useHours.setEnabled(true);
m_useHours.setSelected(true);
break;
case MINUTES:
m_useDate.setSelected(true);
m_useDate.setEnabled(true);
m_startDate.setEnabled(true);
m_hours.setValue(hours);
m_minutes.setValue(minutes);
m_seconds.setEnabled(false);
m_useSeconds.setEnabled(true);
m_minutes.setEnabled(true);
m_useMinutes.setEnabled(true);
m_useMinutes.setSelected(true);
m_hours.setEnabled(true);
m_useHours.setEnabled(true);
m_useHours.setSelected(true);
break;
case SECONDS:
m_useDate.setSelected(true);
m_useDate.setEnabled(true);
m_startDate.setEnabled(true);
m_hours.setValue(hours);
m_minutes.setValue(minutes);
m_seconds.setValue(seconds);
m_seconds.setEnabled(true);
m_useSeconds.setEnabled(true);
m_useSeconds.setSelected(true);
m_minutes.setEnabled(true);
m_useMinutes.setEnabled(true);
m_useMinutes.setSelected(true);
m_hours.setEnabled(true);
m_useHours.setEnabled(true);
m_useHours.setSelected(true);
break;
case NOTIME:
default:
m_startDate.setEnabled(true);
m_useDate.setEnabled(true);
m_useDate.setSelected(true);
m_seconds.setEnabled(false);
m_useSeconds.setEnabled(false);
m_minutes.setEnabled(false);
m_useMinutes.setEnabled(false);
m_hours.setEnabled(false);
m_useHours.setEnabled(true);
m_useDate.setEnabled(true);
m_useDate.setSelected(true);
break;
}
}
/**
* Constructs a new optional DateInputDialog, displaying the fields
* according to the mode.
*
* @param mode
* Specifies the visible time fields in the dialog
*/
public DateInputDialog(final Mode mode) {
this(mode, true);
}
/**
* Constructs a new DateInputDialog, displaying the fields according to the
* mode. Furthermore the date can be mandatory (optional = false) or or
* optional (optional = true) in this case the user can skip specify a Date.
*
* @param mode
* enum specifying the visible time fields in the dialog
* @param optional
* true, if no date has to be specified by the user.
*/
public DateInputDialog(final Mode mode, final boolean optional) {
switch (mode) {
case HOURS:
m_displayHours = true;
break;
case MINUTES:
m_displayHours = true;
m_displayMinutes = true;
break;
case SECONDS:
m_displayHours = true;
m_displayMinutes = true;
m_displaySeconds = true;
break;
default:
break;
}
m_usedMode = mode;
m_isOptional = optional;
initialize();
}
/**
* Creates a new DateInputDialog, displaying all Fields (date, hour, minute,
* second) and the date is optional.
*
*/
public DateInputDialog() {
m_usedMode = Mode.SECONDS;
this.m_displayHours = true;
this.m_displayMinutes = true;
this.m_displaySeconds = true;
initialize();
}
/**
* This method returns false if no date is specified, in case the component
* is optional.
*
*
* @return true if an date is selected, else false
*/
public boolean isUsed() {
if (m_isOptional) {
return m_useDate.isSelected();
}
return true;
}
/**
* This method returns the selected Date. Not active Fields (hour, minute,
* seconds) are ignored. Therefore the fields in the Date object are set to
* .
*
*
* @return the specified date, or null if optional and no date selected.
*/
public Date getSelectedDate() {
int hours = 0;
int minutes = 0;
int seconds = 0;
if (m_useHours.isSelected()) {
hours = (Integer) m_hours.getValue();
}
if (m_useMinutes.isSelected()) {
minutes = (Integer) m_minutes.getValue();
}
if (m_useSeconds.isSelected()) {
seconds = (Integer) m_seconds.getValue();
}
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.set(m_startDate.getCalendar().get(Calendar.YEAR), m_startDate.getCalendar().get(Calendar.MONTH),
m_startDate.getCalendar().get(Calendar.DAY_OF_MONTH), hours, minutes, seconds);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
/**
*
* This method converts the status code to the according enum
* representation.
*
* @param status
* - Status to convert
* @return Enum representation for the given status code.
*/
public static Mode getModeForStatus(final int status) {
if (status == 0) {
return Mode.NODATE;
}
if (status == 1) {
return Mode.NOTIME;
}
if (status == 2) {
return Mode.HOURS;
}
if (status == 3) {
return Mode.MINUTES;
}
return Mode.SECONDS;
}
/**
* This method return an integer Status code, which reflects the currently
* active fields in the dialog.
*
* 0: no Date selected 1: Date but not hours 2: Date and Hours, no
* Minutes... 3: Date, hours and Minutes 4: Everything is selected
*
* The status code can then be converted in the Mode enum via
* getModeForStatus-Method.
*
* @return status code for the currently selected fields
*/
public int getIntForStatus() {
if (!m_useDate.isSelected()) {
return 0;
}
if (!m_useHours.isSelected()) {
return 1;
}
if (!m_useMinutes.isSelected()) {
return 2;
}
if (!m_useSeconds.isSelected()) {
return 3;
}
return 4;
}
/**
* This method initializes the dialog Component, with standard values. Which
* are later on overwritten by the load method. All possible JComponets are
* created and initialized nevertheless they might not be used due to
* different configurations.
*/
private void initialize() {
m_useSeconds = new JCheckBox();
m_useMinutes = new JCheckBox();
m_useHours = new JCheckBox();
m_useDate = new JCheckBox();
m_hours = new JSpinner(new SpinnerNumberModel(0, 0, 23, 1));
m_hours.setMaximumSize(new Dimension(SPINNER_WIDTH, 25));
m_hours.setMinimumSize(new Dimension(SPINNER_WIDTH, 25));
m_minutes = new JSpinner(new SpinnerNumberModel(0, 0, 59, 1));
m_minutes.setMaximumSize(new Dimension(SPINNER_WIDTH, 25));
m_minutes.setMinimumSize(new Dimension(SPINNER_WIDTH, 25));
m_seconds = new JSpinner(new SpinnerNumberModel(0, 0, 59, 1));
m_seconds.setMaximumSize(new Dimension(SPINNER_WIDTH, 25));
m_seconds.setMinimumSize(new Dimension(SPINNER_WIDTH, 25));
m_startDate = new JDateChooser();
m_startDate.setLocale(Locale.US);
m_startDate.getJCalendar().getCalendar().setTimeZone(TimeZone.getTimeZone("UTC"));
// JDateChooser fix
try {
Field popup = m_startDate.getClass().getDeclaredField("popup");
popup.setAccessible(true);
JPopupMenu popupMenu = (JPopupMenu) popup.get(m_startDate);
popupMenu.setFocusable(false);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end fix
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setMinimumSize(new Dimension(PANEL_WIDTH, 60));
Box outerBox = Box.createVerticalBox();
Box dateBox = Box.createHorizontalBox();
if (m_isOptional) {
dateBox.add(m_useDate);
dateBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
}
dateBox.add(m_startDate);
dateBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
dateBox.setPreferredSize(new Dimension(PANEL_WIDTH, 25));
dateBox.setMaximumSize(new Dimension(PANEL_WIDTH, 25));
dateBox.setMinimumSize(new Dimension(PANEL_WIDTH, 25));
outerBox.add(dateBox);
Box timeBox = Box.createHorizontalBox();
if (m_displayHours) {
timeBox.add(m_useHours);
timeBox.add(new JLabel("Hour: "));
timeBox.add(m_hours);
timeBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
timeBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
if (m_displayMinutes) {
timeBox.add(m_useMinutes);
timeBox.add(new JLabel("Minute: "));
timeBox.add(m_minutes);
timeBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
timeBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
if (m_displaySeconds) {
timeBox.add(m_useSeconds);
timeBox.add(new JLabel("Second: "));
timeBox.add(m_seconds);
timeBox.add(Box.createHorizontalStrut(HORIZ_SPACE));
}
}
}
timeBox.add(Box.createHorizontalGlue());
outerBox.setMaximumSize(new Dimension(PANEL_WIDTH, 60));
outerBox.setMinimumSize(new Dimension(PANEL_WIDTH, 60));
outerBox.add(Box.createVerticalStrut(VERT_SPACE));
outerBox.add(timeBox);
if (m_isOptional) {
m_startDate.setEnabled(false);
m_useHours.setEnabled(false);
} else {
m_startDate.setEnabled(true);
m_useHours.setEnabled(true);
}
m_useHours.setSelected(false);
m_hours.setEnabled(false);
m_useMinutes.setEnabled(false);
m_minutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_seconds.setEnabled(false);
m_useDate.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_useDate.isSelected()) {
m_startDate.setEnabled(true);
m_useHours.setEnabled(true);
} else {
m_startDate.setEnabled(false);
m_startDate.cleanup();
m_useHours.setSelected(false);
m_useHours.setEnabled(false);
m_hours.setEnabled(false);
m_useMinutes.setEnabled(false);
m_useMinutes.setSelected(false);
m_minutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_useSeconds.setSelected(false);
m_seconds.setEnabled(false);
}
}
});
m_useHours.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_useHours.isSelected()) {
m_hours.setEnabled(true);
m_useMinutes.setEnabled(true);
m_minutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_seconds.setEnabled(false);
} else {
m_hours.setEnabled(false);
m_useMinutes.setEnabled(false);
m_useMinutes.setSelected(false);
m_minutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_useSeconds.setSelected(false);
m_seconds.setEnabled(false);
}
}
});
m_useMinutes.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_useMinutes.isSelected()) {
m_minutes.setEnabled(true);
m_useSeconds.setEnabled(true);
m_seconds.setEnabled(false);
} else {
m_useMinutes.setSelected(false);
m_minutes.setEnabled(false);
m_useSeconds.setEnabled(false);
m_useSeconds.setSelected(false);
m_seconds.setEnabled(false);
m_seconds.setEnabled(false);
}
}
});
m_useSeconds.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_useSeconds.isSelected()) {
m_seconds.setEnabled(true);
} else {
m_seconds.setEnabled(false);
}
}
});
add(outerBox);
add(Box.createVerticalStrut(VERT_SPACE));
}
}