All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.sfac.gui.cmp.DateTimeChooser Maven / Gradle / Ivy

Go to download

This project is the core part of the Swing Framework and Components (SFaC). It contains the Swing framework classes and the graphical component classes.

The newest version!
/*-------------------------------------------------------------------------
 Copyright 2009 Olivier Berlanger

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -------------------------------------------------------------------------*/
package net.sf.sfac.gui.cmp;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextField;
import javax.swing.border.Border;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * This class is a dialog where a date can be selected interactively. Calendar settings like names of months, days and order of days
 * in the week are taken from the locale.
 * 
 * @author Olivier Berlanger
 */
@SuppressWarnings("serial")
public class DateTimeChooser extends JDialog {

    private static final Log log = LogFactory.getLog(DateTimeChooser.class);

    private static final int PREFERRED_WIDTH = 296;
    private static final int PREFERRED_HEIGHT_WITH_TIME = 320;
    private static final int PREFERRED_HEIGHT_NO_TIME = 280;

    /** Default foreground color for day labels. */
    private static final Color DEFAULT_FOREGROUND = Color.gray;
    /** Default background color for day labels. */
    private static final Color DEFAULT_BACKGROUND = Color.white;
    /** Default background color for week-end day labels. */
    private static final Color WEEK_END_BACKGROUND = new Color(240, 240, 250);
    /** Foreground color for the current day label. */
    private static final Color SELECTED_FOREGROUND = Color.white;
    /** Background color for the current day label. */
    private static final Color SELECTED_BACKGROUND = Color.orange;

    /** Calendar used by this chooser it stores the current selected value (it's the model). */
    private Calendar cal;
    /** Selected date, this field is set only when the user press the OK button. */
    private Date selectedDate;

    /** Combo box for selecting months. */
    private JComboBox monthCombo;
    /** Labels displaying the days of the current month. */
    private JLabel[] dayLabels;
    /** Panel holding day labels. */
    private JPanel daysPanel;
    /** Text field editing the year. */
    private JTextField yearTextField;
    /** ScrollBar used as spin buttons to edit the year. */
    private JScrollBar yearUpDn;
    /** Text field editing the hour. */
    private JTextField hourTextField;
    /** ScrollBar used as spin buttons to edit the hour. */
    private JScrollBar hourUpDn;
    /** Text field editing the minutes. */
    private JTextField minuteTextField;
    /** ScrollBar used as spin buttons to edit the minutes. */
    private JScrollBar minuteUpDn;
    /** Text field editing the seconds. */
    private JTextField secondTextField;
    /** ScrollBar used as spin buttons to edit the seconds. */
    private JScrollBar secondUpDn;

    /** The current selected day label. */
    private JLabel selectedLabel;
    /** The backgroud color of the current selected day label before it was selected. */
    private Color selectedLabelBackround;


    /**
     * Construct a chooser dialog with a frame as parent.
     * 
     * @param parent
     *            the parent frame
     * @param title
     *            title of the chooser dialog
     * @param showTime
     *            true iff the dialog should edit time (hour,min,sec)
     */
    public DateTimeChooser(Frame parent, String title, boolean showTime) {
        super(parent, title, true);
        init(showTime);
    }


    /**
     * Construct a chooser dialog with a dialog as parent.
     * 
     * @param parent
     *            the parent dialog
     * @param title
     *            title of the chooser dialog
     * @param showTime
     *            true iff the dialog should edit time (hour,min,sec)
     */
    public DateTimeChooser(Dialog parent, String title, boolean showTime) {
        super(parent, title, true);
        init(showTime);
    }


    /**
     * Show this dialog to edit the given date.
     * 
     * @param initialDate
     *            The date to edit (if null current date is used)
     * @return The edited date selected by the user or initialDate if the user cancel the editing.
     */
    public Date editDate(Date initialDate) {
        if (initialDate == null) initialDate = new Date();
        setDate(initialDate);
        setVisible(true);
        Date resultDate = getSelectedDate();
        return (resultDate == null) ? initialDate : resultDate;
    }


    /**
     * Get the date selected by the user, returns null if the user did not press the OK button.
     * 
     * @return the selected Date or null if edition was cancelled.
     */
    public Date getSelectedDate() {
        return selectedDate;
    }


    /**
     * Set the initial date to edit.
     * 
     * @param newDate
     *            the initial date to edit.
     */
    public void setDate(Date newDate) {
        cal.setTime(newDate);
        updateView();
    }


    /**
     * Init the UI and configure this dialog.
     * 
     * @param showTime
     *            true iff the components to edit time (hour, min, sec) should be displayed.
     */
    private void init(boolean showTime) {
        cal = Calendar.getInstance();
        createUI(showTime);
        setSize(new Dimension(PREFERRED_WIDTH, (showTime) ? PREFERRED_HEIGHT_WITH_TIME : PREFERRED_HEIGHT_NO_TIME));
        setResizable(false);
    }


    /**
     * Create the contentPane of this dialog (and all it's content).
     * 
     * @param showTime
     *            true iff the components to edit time (hour, min, sec) should be displayed.
     */
    private void createUI(boolean showTime) {
        DateFormatSymbols dfs = new DateFormatSymbols();
        // create the panels holding components
        JPanel topPanel = createTopPanel(dfs);
        daysPanel = createDaysPanel(dfs);
        JPanel bottomPanel;
        if (showTime) {
            JPanel hourPanel = createHourPanel();
            JPanel buttonsPanel = createButtonsPanel();
            bottomPanel = new JPanel(new GridLayout(2, 1));
            bottomPanel.add(hourPanel);
            bottomPanel.add(buttonsPanel);
        } else {
            bottomPanel = createButtonsPanel();
        }
        // Assemble everything in the frame
        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.add(topPanel, BorderLayout.NORTH);
        contentPane.add(daysPanel, BorderLayout.CENTER);
        contentPane.add(bottomPanel, BorderLayout.SOUTH);
        setContentPane(contentPane);
    }


    /**
     * Create the panel showing the month combo and year editor.
     * 
     * @param dfs
     *            The DateFormatSymbols for the current locale.
     * @return the panel showing the month combo and year editor
     */
    private JPanel createTopPanel(DateFormatSymbols dfs) {
        JPanel topPanel = new JPanel(new GridBagLayout());
        // create month combo box
        String[] monthNames = dfs.getMonths();
        monthCombo = new JComboBox();
        int maxMonth = cal.getMaximum(Calendar.MONTH);
        for (int i = 0; i <= maxMonth; i++)
            monthCombo.addItem(monthNames[i]);
        monthCombo.addItemListener(new ItemListener() {

            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) monthChanged();
            }
        });
        // Configure Year Selector
        yearTextField = new JTextField(5);
        yearUpDn = new JScrollBar();
        addSelector(topPanel, yearTextField, yearUpDn, 0, null, Calendar.YEAR, 'Y', 5000);
        // put all on top panel
        topPanel.add(monthCombo, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
                new Insets(10, 5, 5, 5), 0, 0));
        topPanel.add(yearTextField, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
                GridBagConstraints.NONE, new Insets(10, 0, 5, 0), 0, 0));
        topPanel.add(yearUpDn, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
                new Insets(10, 0, 5, 5), 0, 0));
        return topPanel;
    }


    /**
     * Create the panel editing the time (hour, min, sec).
     * 
     * @return the panel editing the time (hour, min, sec)
     */
    private JPanel createHourPanel() {
        JPanel hourPanel = new JPanel(new GridBagLayout());
        hourTextField = new JTextField(3);
        hourUpDn = new JScrollBar();
        addSelector(hourPanel, hourTextField, hourUpDn, 0, "Hour:", Calendar.HOUR_OF_DAY, 'H', 23);
        minuteTextField = new JTextField(3);
        minuteUpDn = new JScrollBar();
        addSelector(hourPanel, minuteTextField, minuteUpDn, 3, "Min.:", Calendar.MINUTE, 'M', 59);
        secondTextField = new JTextField(3);
        secondUpDn = new JScrollBar();
        addSelector(hourPanel, secondTextField, secondUpDn, 6, "Sec.:", Calendar.SECOND, 'S', 59);
        return hourPanel;
    }


    /**
     * Configure a (optional) label, a textField and a scrollbar (used as spin buttons) to edit a field value of the calendar (year,
     * hour, min, sec). Add those components to a panel using a GridBagLayout.
     * 
     * @param panel
     *            the panel where to add components
     * @param textField
     *            the text field to configure
     * @param spin
     *            the scrollbar to configure
     * @param index
     *            the start X index to use in gridBagLayout
     * @param label
     *            the label for the create JLabel (no label created if null)
     * @param fieldId
     *            the edited field id in Calendar
     * @param mnemonic
     *            the label mnemonic
     * @param maxValue
     *            the max value for the scrollbar
     */
    private void addSelector(JPanel panel, final JTextField textField, final JScrollBar spin, int index, String label,
            final int fieldId, char mnemonic, int maxValue) {
        textField.setHorizontalAlignment(JTextField.RIGHT);
        textField.addFocusListener(new FocusAdapter() {

            @Override
            public void focusLost(FocusEvent e) {
                fieldChanged(fieldId, textField, spin);
            }
        });
        textField.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) fieldChanged(fieldId, textField, spin);
            }
        });
        JLabel fieldLabel;
        if (label != null) {
            fieldLabel = new JLabel(label);
            fieldLabel.setDisplayedMnemonic(mnemonic);
            fieldLabel.setLabelFor(textField);
        } else {
            fieldLabel = null;
        }
        int textFieldHeight = textField.getPreferredSize().height;
        spin.setMinimum(0);
        spin.setMaximum(maxValue + 10);
        spin.setUnitIncrement(-1);
        spin.setBlockIncrement(1);
        spin.setPreferredSize(new Dimension(20, textFieldHeight));
        spin.addAdjustmentListener(new AdjustmentListener() {

            public void adjustmentValueChanged(AdjustmentEvent e) {
                fieldSpinClicked(fieldId, textField, spin);
            }
        });
        if (fieldLabel != null) panel.add(fieldLabel, new GridBagConstraints(index, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
                GridBagConstraints.NONE, new Insets(8, 5, 0, 5), 0, 0));
        panel.add(textField, new GridBagConstraints(index + 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
                GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0));
        panel.add(spin, new GridBagConstraints(index + 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
                new Insets(5, 0, 0, 0), 0, 0));
    }


    /**
     * Create the panel showing the days in the current month.
     * 
     * @param dfs
     *            The DateFormatSymbols for the current locale.
     * @return the panel showing the days in the current month.
     */
    private JPanel createDaysPanel(DateFormatSymbols dfs) {
        JPanel daysSelectionPanel = new JPanel(new GridLayout(7, 7, 1, 1));
        Border outBorder = BorderFactory.createEmptyBorder(5, 10, 5, 10);
        Border inBorder = BorderFactory.createLoweredBevelBorder();
        daysSelectionPanel.setBorder(BorderFactory.createCompoundBorder(outBorder, inBorder));
        // create the labels for days (Sun., Mon., ...)
        String[] dayNames = dfs.getShortWeekdays();
        int firstDayOfWeek = cal.getFirstDayOfWeek();
        int sundayColumn = -1;
        int saturdayColumn = -1;
        for (int i = firstDayOfWeek; i < firstDayOfWeek + Calendar.SATURDAY; i++) {
            int nameIndex = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
            if (nameIndex == Calendar.SUNDAY) sundayColumn = i - firstDayOfWeek;
            if (nameIndex == Calendar.SATURDAY) saturdayColumn = i - firstDayOfWeek;
            daysSelectionPanel.add(new JLabel(dayNames[nameIndex], JLabel.CENTER));
        }
        // add labels for each day in the month
        dayLabels = new JLabel[42];
        MouseListener lis = new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent me) {
                dayChanged((JLabel) me.getSource());
            }
        };
        for (int i = 0; i < 42; i++) {
            dayLabels[i] = new JLabel(String.valueOf(i), JLabel.CENTER);
            dayLabels[i].setForeground(DEFAULT_FOREGROUND);
            dayLabels[i].addMouseListener(lis);
            int column = i % 7;
            boolean weekEnd = (column == saturdayColumn) || (column == sundayColumn);
            dayLabels[i].setBackground(weekEnd ? WEEK_END_BACKGROUND : DEFAULT_BACKGROUND);
            daysSelectionPanel.add(dayLabels[i]);
        }
        return daysSelectionPanel;
    }


    /**
     * Create the panel showing the OK and Cancel buttons.
     * 
     * @return the panel showing the OK and Cancel buttons.
     */
    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        buttonsPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
        // Configure OK Button
        JButton okButton = new JButton("Ok");
        okButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                closeWithOk();
            }
        });
        // Configure Cancel Button
        JButton cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                setVisible(false);
            }
        });
        // add on buttons panel
        buttonsPanel.add(okButton);
        buttonsPanel.add(cancelButton);
        return buttonsPanel;
    }


    /**
     * Notification that a field value has changed in the editing textField.
     * 
     * @param fieldId
     *            the field ID in Calendar
     * @param textField
     *            the editing textField.
     * @param spinButtons
     *            the editing spin buttons.
     */
    void fieldChanged(int fieldId, JTextField textField, JScrollBar spinButtons) {
        String txtValue = textField.getText();
        try {
            int value = Integer.parseInt(txtValue);
            cal.set(fieldId, value);
        } catch (Exception e) {
            log.warn("Exception in fieldValueChanged event", e);
        }
        updateViewForFieldChange(fieldId, textField, spinButtons);
    }


    /**
     * Notification that a field value has changed in the editing spin buttons.
     * 
     * @param fieldId
     *            the field ID in Calendar
     * @param textField
     *            the editing textField.
     * @param spinButtons
     *            the editing spin buttons.
     */
    void fieldSpinClicked(int fieldId, JTextField textField, JScrollBar spinButtons) {
        cal.set(fieldId, spinButtons.getValue());
        updateViewForFieldChange(fieldId, textField, spinButtons);
    }


    /**
     * Notification that a field value has changed in the model (= the calendar). Update the corresponding editing fields. If the
     * changed field is the year, fire monthChanged().
     * 
     * @param fieldId
     *            the field ID in Calendar
     * @param textField
     *            the editing textField.
     * @param spinButtons
     *            the editing spin buttons.
     */
    private void updateViewForFieldChange(int fieldId, JTextField textField, JScrollBar spinButtons) {
        if (textField != null) {
            int value = cal.get(fieldId);
            textField.setText(String.valueOf(value));
            spinButtons.setValue(value);
            if (fieldId == Calendar.YEAR) monthChanged();
        }
    }


    /**
     * Notification that the month has changed in the month combo or that the year has changed (so in this case the month is the
     * same in the combo but the 'real' month has changed).
     */
    void monthChanged() {
        int currentDay = cal.get(Calendar.DAY_OF_MONTH);
        int month = monthCombo.getSelectedIndex();
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.MONTH, month);
        // set the days labels for the new mounth
        int firstDayOfWeek = cal.getFirstDayOfWeek();
        int dayOfWeekOfFirstDay = cal.get(Calendar.DAY_OF_WEEK);
        int maxDayOfMonth = cal.getActualMaximum(GregorianCalendar.DAY_OF_MONTH);
        if (currentDay > maxDayOfMonth) currentDay = maxDayOfMonth;
        cal.set(Calendar.DAY_OF_MONTH, currentDay);
        int startIndex = dayOfWeekOfFirstDay - firstDayOfWeek;
        if (startIndex < 0) startIndex += 7;
        int endIndex = startIndex + maxDayOfMonth - 1;
        for (int i = 0; i < 42; i++) {
            if ((i < startIndex) || (i > endIndex)) {
                dayLabels[i].setText("");
                dayLabels[i].setOpaque(false);
            } else {
                int day = (i - startIndex) + 1;
                dayLabels[i].setText(String.valueOf(day));
                dayLabels[i].setOpaque(true);
                if (day == currentDay) highlightLabel(dayLabels[i]);
            }
        }
        daysPanel.repaint();
    }


    /**
     * Notification that the user pressed the OK button.
     */
    void closeWithOk() {
        selectedDate = cal.getTime();
        setVisible(false);
    }


    /**
     * Notification that a label showing day was selecetd by the user.
     * 
     * @param lbl
     *            the label showing day selected.
     */
    void dayChanged(JLabel lbl) {
        String labelText = lbl.getText();
        if ((labelText != null) && !labelText.equals("")) {
            int day = Integer.parseInt(labelText);
            cal.set(Calendar.DAY_OF_MONTH, day);
            highlightLabel(lbl);
        }
    }


    /**
     * Update all the view components with the value currently in the model (= the calendar).
     */
    private void updateView() {
        monthCombo.setSelectedIndex(cal.get(Calendar.MONTH));
        updateViewForFieldChange(Calendar.YEAR, yearTextField, yearUpDn);
        updateViewForFieldChange(Calendar.HOUR_OF_DAY, hourTextField, hourUpDn);
        updateViewForFieldChange(Calendar.MINUTE, minuteTextField, minuteUpDn);
        updateViewForFieldChange(Calendar.SECOND, secondTextField, secondUpDn);
    }


    /**
     * Highlight the specified day label and restore the state of the currently highlighted label.
     * 
     * @param label
     *            the label to highlight.
     */
    private void highlightLabel(JLabel label) {
        // restore state of currently selected label
        if (selectedLabel != null) {
            selectedLabel.setForeground(DEFAULT_FOREGROUND);
            selectedLabel.setBackground(selectedLabelBackround);
        }
        // highlight the new label
        if (label != null) {
            selectedLabelBackround = label.getBackground();
            label.setForeground(SELECTED_FOREGROUND);
            label.setBackground(SELECTED_BACKGROUND);
        }
        selectedLabel = label;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy