/* JdateField.java This class defines a date input field object. Created: 31 Jul 1996 Module By: Navin Manohar ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2013 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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/>. */ package arlut.csd.JDataComponent; import java.awt.BorderLayout; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.rmi.RemoteException; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.JFormattedTextField; import javax.swing.JTextField; import org.jdesktop.swingx.JXDatePicker; import org.jdesktop.swingx.JXMonthView; import org.jdesktop.swingx.plaf.basic.SpinningCalendarHeaderHandler; import org.jdesktop.swingx.plaf.basic.CalendarHeaderHandler; import arlut.csd.JDialog.StandardDialog; import arlut.csd.JDialog.JErrorDialog; import arlut.csd.Util.PackageResources; import arlut.csd.Util.TranslationService; /*------------------------------------------------------------------------------ class JdateField ------------------------------------------------------------------------------*/ /** * <p>This class defines a Date/Time GUI component that ties into the * {@link arlut.csd.JDataComponent.JsetValueCallback} interface that * the Ganymede clients use internally to receive higher level GUI * events.</p> */ public class JdateField extends JPanel implements ActionListener, FocusListener { static final boolean debug = false; /** * TranslationService object for handling string localization in the * Ganymede client. */ static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.JDataComponent.JdateField"); // --- /** * The SwingX date picker GUI component we're building JdateField around. */ private JXDatePicker datePicker; /** * datePicker's internal text editing field, that we apply a * FocusListener to. */ private JFormattedTextField datef; /** * A text editing component for the time of day for this JdateField, * if we are set up to provide time display/editing. */ private JTextField timef; /** * The callback that we use to communicate date changes in this * field to the Ganymede client, etc. */ private JsetValueCallback callback = null; /** * The last date that we had from the Ganymede server. We will * revert to this date if our callback refuses our change. */ private Date original_date; /** * The date that we most recently have from our GUI components. * This will be promoted into original_date once a callback carrying * this date succeeds. */ private Date curr_date; /* -- */ /** * Minimal Constructor for JdateField. This will construct a JdateField * with no value. */ public JdateField() { this(null,true,false,true,null,null); } /** * Contructor that creates a JdateField based on the Date object it is given. This * constructor can be used if the JdateField will be making callbacks to pass its data * to the appropriate container. * * @param parent the container which implements the callback function for this JdateField * @param date the Date object to use * @param iseditable true if the datefield can be edited by the user * @param islimited true if there is to be a restriction on the range of dates * @param usetime If true, this JdateField will display a time edit box next to the date edit field. * @param minDate the oldest possible date that can be entered into this JdateField * @param maxDate the newest possible date that can be entered into this JdateField */ public JdateField(Date date, boolean iseditable, boolean islimited, boolean usetime, Date minDate, Date maxDate, JsetValueCallback parent) { this(date,iseditable,islimited,usetime,minDate,maxDate); setCallback(parent); } /** * Contructor that creates a JdateField based on the date it is given. It is also * possible to set restrictions on the range of dates for this JdateField when * using this constructor * * @param date the Date object to use * @param islimited true if there is to be a restriction on the range of dates * @param usetime If true, this JdateField will display a time edit box next to the date edit field. * @param minDate the oldest possible date that can be entered into this JdateField * @param maxDate the newest possible date that can be entered into this JdateField */ public JdateField(Date date, boolean iseditable, boolean islimited, boolean usetime, Date minDate, Date maxDate) { if (debug) { System.err.println("JdateField(): date = " + date); } if (date == null) { curr_date = original_date = null; } else { curr_date = original_date = new Date(date.getTime()); } if (islimited) { if (minDate == null) { throw new IllegalArgumentException("Invalid Parameter: minDate cannot be null"); } if (maxDate == null) { throw new IllegalArgumentException("Invalid Parameter: maxDate canot be null"); } } setLayout(new BorderLayout()); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BorderLayout()); // Adds a year spinner to the MonthView object. UIManager.put(CalendarHeaderHandler.uiControllerID, "org.jdesktop.swingx.plaf.basic.SpinningCalendarHeaderHandler"); // Moves the year spinner after month arrows. UIManager.put(SpinningCalendarHeaderHandler.ARROWS_SURROUND_MONTH, Boolean.TRUE); datePicker = new JXDatePicker(date); datePicker.setName("datePicker"); datePicker.setEditable(iseditable); datePicker.addActionListener(this); JXMonthView monthView = datePicker.getMonthView(); if (islimited) { if (minDate != null) { monthView.setLowerBound(minDate); } if (maxDate != null) { monthView.setUpperBound(maxDate); } } monthView.setZoomable(true); datef = datePicker.getEditor(); if (iseditable) { datef.addFocusListener(this); } buttonPanel.add(datePicker, "West"); // Add in a time input. timef = new JTextField(5); timef.setEditable(iseditable); // Add focus listener. if (usetime) { if (iseditable) { timef.addFocusListener(this); } add(timef); } add(buttonPanel, "East"); setDate(curr_date); invalidate(); validate(); } /** * FocusListener method to react to focus loss on the datePicker * and/or timef text editing widgets. */ public void focusLost(FocusEvent e) { Object c = e.getSource(); if (c == timef) { setTimeOnly(timef.getText()); updateServer(); } else if (c == datef) { try { datePicker.getEditor().commitEdit(); setDateOnly(datePicker.getDate()); updateServer(); } catch (ParseException pe) { } } } /** * Required by the FocusListener interface. */ public void focusGained(FocusEvent e) { JComponent parent = (JComponent) this.getParent(); if (parent != null) { parent.scrollRectToVisible(this.getBounds()); } } /** * ActionListener method we use to trigger on mouse clicks on the drop-down calendar widget. */ public void actionPerformed(ActionEvent e) { Object c = e.getSource(); if (c == datePicker) { setDateOnly(datePicker.getDate()); updateServer(); } } /** * Returns the date associated with this JdateField */ public Date getDate() { return curr_date; } /** * Sets the minimum and maximum dates allowed in this JdateField. */ public void setLimits(Date minDate, Date maxDate) { JXMonthView monthView = datePicker.getMonthView(); monthView.setLowerBound(minDate); monthView.setUpperBound(maxDate); } /** * <p>Sets the calendar date of this JdateField, keeping the time of * day previously set.</p> * * <p>Calling this method does not trigger the callback.</p> * * @param date The Date to load into this JdateField. */ private void setDateOnly(Date date) { if (date != null) { if (debug) { System.err.println("setDateOnly() called: " + date); } if (curr_date == null) { setDate(date); return; } Calendar cal = Calendar.getInstance(); cal.setTime(curr_date); int hour = cal.get(Calendar.HOUR_OF_DAY); int minute = cal.get(Calendar.MINUTE); cal.setTime(date); cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.MINUTE, minute); setDate(cal.getTime()); } else { setDate(null); } } /** * <p>Sets the time of day for this JdateField, keeping the calendar * date previously set.</p> * * <p>Calling this method does not trigger the callback.</p> * * @param timeStr The string to extract time of day from. */ private void setTimeOnly(String timeStr) { if (timeStr == null) { return; } String[] splt = timeStr.split(":"); if (splt.length < 2) { return; } Calendar cal = Calendar.getInstance(); if (curr_date == null) { // oops, we can't set the time without some underlying // date.. pick the current date then set the time on it. cal.setTime(new Date()); } else { cal.setTime(curr_date); } cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(splt[0])); cal.set(Calendar.MINUTE, Integer.parseInt(splt[1])); setDate(cal.getTime()); } /** * <p>Sets the date and time value of this JdateField.</p> * * <p>Calling this method does not trigger the callback.</p> * * @param date The Date to extract the calendar date and time of * day from. */ public void setDate(Date date) { if (debug) { System.err.println("setDate() called: " + date); } datePicker.setDate(date); if (date != null) { Calendar cal = Calendar.getInstance(); cal.setTime(date); String hour = prefixZero(Integer.toString(cal.get(Calendar.HOUR_OF_DAY))); String minute = prefixZero(Integer.toString(cal.get(Calendar.MINUTE))); timef.setText(hour + ":" + minute); } else { timef.setText("00:00"); } curr_date = date; } /** * Attaches this JdateField component to the callback we use to * notify on date/time change input. */ public void setCallback(JsetValueCallback callback) { this.callback = callback; } /** * Propagate the date value up to the server object. */ public void updateServer() { if (callback == null) { return; } // Right now, we don't support using the JdateField GUI component // to set a date field on the server to null, nor do we have any // reason to let the JdateField be used to re-transmit the // currently set date to the server again. if (curr_date == null || (original_date != null && curr_date.compareTo(original_date) == 0)) { return; } boolean retval = false; try { retval = callback.setValuePerformed(new JSetValueObject(this, curr_date)); } catch (RemoteException ex) { // throw up an information dialog here // "Date Field Error" // "There was an error communicating with the server!\n{0}" new JErrorDialog(new JFrame(), ts.l("global.error_subj"), ts.l("global.error_text", ex.getMessage()), StandardDialog.ModalityType.DOCUMENT_MODAL); } // if setValuePerformed() didn't work, revert the date, if (!retval) { setDate(original_date); return; } // Now, the new value has propagated to the server, so reset // original date, so that the next time we loose focus from // this widget, we won't unnecessarily update the server value // if nothing has changed locally. original_date = curr_date; } private String prefixZero(String str) { if (str.length() < 2) { str = "0" + str; } return str; } }