/*
JpanelCalendar.java
A GUI Calendar for use with the arlut.csd.JDataComponent JdateField class.
Created: 17 March 1997
Module By: Navin Manohar, Michael Mulvaney, and Jonathan Abbey
-----------------------------------------------------------------------
Ganymede Directory Management System
Ganymede is a registered trademark of The University of Texas at Austin
Copyright (C) 1996-2013
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.JCalendar;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.rmi.RemoteException;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import arlut.csd.JDataComponent.JSetValueObject;
import arlut.csd.JDataComponent.JValueObject;
import arlut.csd.JDataComponent.JnumberField;
import arlut.csd.JDataComponent.JResetDateObject;
import arlut.csd.JDataComponent.JsetValueCallback;
import arlut.csd.JDataComponent.TimedKeySelectionManager;
import arlut.csd.JDialog.JErrorDialog;
import arlut.csd.JDialog.StandardDialog;
import arlut.csd.Util.PackageResources;
import arlut.csd.Util.TranslationService;
/*------------------------------------------------------------------------------
class
JpanelCalendar
------------------------------------------------------------------------------*/
/**
* <p>A GUI Calendar for use with the {@link
* arlut.csd.JDataComponent.JdateField JdateField} class.</p>
*/
public class JpanelCalendar extends JPanel implements ActionListener {
static final boolean debug = false;
/**
* <p>TranslationService object for handling string localization in
* the Ganymede system.</p>
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.JCalendar.JpanelCalendar");
/**
* <p>Localized DateFormatSymbols for us to use appropriate month
* and week day strings.</p>
*/
static final DateFormatSymbols symbolTable = new DateFormatSymbols();
static final int leapDays[] = {31,29,31,30,31,30,31,31,30,31,30,31};
static final int monthDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static final String[] month_names = symbolTable.getMonths();
// ---
/**
* What time do we have set?
*/
protected GregorianCalendar selectedDate_calendar;
/**
* What time do we have visible? Month, year, etc.
*/
protected GregorianCalendar visibleDate_calendar;
/**
* <p>Who do we notify when the user changes the date through
* direct manipulation of the calendar?</p>
*/
protected JsetValueCallback callback;
/**
* <p>If we are contained in a pop-up, this will refer to
* the dialog frame, so that the close button can close
* it.</p>
*/
protected JpopUpCalendar pCal = null;
protected JButton closeButton;
protected JButton resetButton;
/**
* <p>The meat of the calendar. This array of JdateButton's
* are both the display and the main user interface element for the
* JpanelCalendar.</p>
*/
protected JdateButton _datebuttonArray[] = new JdateButton[37];
private JMonthYearPanel monthYearPanel = null;
private JPanel calButtonPanel = null;
private JPanel buttonPanel = null;
private JTimePanel timePanel;
protected Font todayFont = new Font("Monospaced", Font.BOLD, 12);
protected Font notTodayFont = new Font("Monospaced", Font.PLAIN, 12);
/**
* <p>If true, we will allow the calendar to be used to change the
* date/time selected. If false, we will be display only.</p>
*/
private boolean editable;
/**
* <p>Used to control whether we show a selected day in the
* calendar. If false, no date has been set, and we'll
* show all calendar pages with no dates highlighed.</p>
*/
private boolean dateIsSet;
/**
* <p>If true, we'll show the time of day in the calendar, and allow
* the user to edit the time of day if we are editable. If false,
* we'll show the the date only.</p>
*/
private boolean showTime;
/**
* <p>If true, we'll allow the use to change the month and year
* displayed in the calendar.</p>
*/
private boolean allowMonthChange;
/**
* <p>If true, we'll try to render the calendar in a compressed
* form, with less space for the calendar buttons.</p>
*/
private boolean compact;
/**
* <p>The last known good date. If we attempt to pass a date change
* back to our client and the attempt fails, this is the date we'll
* revert to afterwards.</p>
*/
private Date previousDate;
/**
* Private flag to inhibit updating while we're modifying
* our calendar's state.
*/
private boolean inhibitPainting = false;
/* -- */
/**
* @param parentCalendar The date to initialize this JpanelCalendar to
* @param callback A callback to receive and approve changes to this JpanelCalendar
* @param editable If false, this JpanelCalendar will be a
* non-changeable calendar for display.
*/
public JpanelCalendar(GregorianCalendar parentCalendar, JsetValueCallback callback, boolean editable)
{
this(parentCalendar, callback, true, false, editable);
}
/**
* Constructors.
*
* @param parentCalendar The date to initialize this JpanelCalendar to
* @param callback A callback to receive and approve changes to this JpanelCalendar
* @param editable If false, this JpanelCalendar will be a
* non-changeable calendar for display.
* @param showTime If true, then the "Choose a time" part will be
* there. Also, if true, time will appear in date at top.
*/
public JpanelCalendar(GregorianCalendar parentCalendar, JsetValueCallback callback,
boolean showTime, boolean editable)
{
this(parentCalendar, callback, showTime, false, editable);
}
public JpanelCalendar(JpopUpCalendar pC, GregorianCalendar parentCalendar,
JsetValueCallback callback, boolean editable)
{
this(pC, parentCalendar, callback, true, false, editable);
}
public JpanelCalendar(JpopUpCalendar pC, GregorianCalendar parentCalendar,
JsetValueCallback callback, boolean showTime,
boolean editable)
{
this(pC, parentCalendar, callback, showTime, false, editable);
}
/**
* @param pC The dialog that this JpanelCalendar will be displayed in.
* @param parentCalendar The date to initialize this JpanelCalendar to
* @param callback A callback to receive and approve changes to this JpanelCalendar
* @param showTime If true, then the "Choose a time" part will be
* there. Also, if true, time will appear in date at top.
* @param compact If true, calendar will be drawn smaller.
* @param editable If false, this JpanelCalendar will be a
* non-changeable calendar for display.
*/
public JpanelCalendar(JpopUpCalendar pC,
GregorianCalendar parentCalendar,
JsetValueCallback callback,
boolean showTime, boolean compact,
boolean editable)
{
this(parentCalendar, callback, showTime, compact, editable);
if (pC == null)
{
throw new IllegalArgumentException("popUpCalendar parameter is null");
}
pCal = pC;
resetButton = new JButton(ts.l("init.resetButton")); // "Reset Date"
buttonPanel.add(resetButton, "West");
closeButton = new JButton(ts.l("init.closeButton")); // "Close"
buttonPanel.add(closeButton,"East");
resetButton.addActionListener(this);
closeButton.addActionListener(this);
}
/**
* <p>The main constructor. Here's where all the magic happens.</p>
*
* @param parentCalendar The date to initialize this JpanelCalendar to
* @param callback A callback to receive and approve changes to this JpanelCalendar
* @param showTime If true, then the "Choose a time" part will be
* there. Also, if true, time will appear in date at top.
* @param compact If true, calendar will be drawn smaller.
* @param editable If false, this JpanelCalendar will be a
* non-changeable calendar for display.
*/
public JpanelCalendar(GregorianCalendar parentCalendar,
JsetValueCallback callback,
boolean showTime, boolean compact,
boolean editable)
{
if (parentCalendar == null)
{
// if we weren't given a calendar object, create a default
// calendar, which will be initialized to current date/time.
parentCalendar = new GregorianCalendar();
dateIsSet = false;
}
else
{
dateIsSet = true;
}
this.showTime = showTime;
this.compact = compact;
this.editable = editable;
if (debug)
{
if (editable)
{
System.err.println("JpanelCalendar.editable == true!");
}
else
{
System.err.println("JpanelCalendar.editable == false!");
}
}
this.callback = callback;
selectedDate_calendar = parentCalendar;
previousDate = selectedDate_calendar.getTime();
dateIsSet = true;
setLayout(new FlowLayout());
// Everything is in the main Panel here
JPanel mainPanel = new JPanel(false);
mainPanel.setLayout(new BorderLayout());
// we start off with our visible calendar page the same as our
// selected date calendar
visibleDate_calendar = (GregorianCalendar) selectedDate_calendar.clone();
// North Panel
monthYearPanel = new JMonthYearPanel(this);
mainPanel.add(monthYearPanel, "North");
JPanel daysPanel = new JPanel(false);
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
daysPanel.setLayout(gbl);
gbc.gridy = 0;
gbc.gridx = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = 8; // seven colums for the days of the week
gbc.ipadx = 5;
gbc.insets = new Insets(10,2,0,2);
JLabel sun = new JLabel(symbolTable.getShortWeekdays()[Calendar.SUNDAY]);
sun.setFont(todayFont);
sun.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 0;
gbc.gridwidth = 1;
gbl.setConstraints(sun, gbc);
daysPanel.add(sun);
JLabel mon = new JLabel(symbolTable.getShortWeekdays()[Calendar.MONDAY]);
mon.setFont(todayFont);
mon.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 1;
gbl.setConstraints(mon, gbc);
daysPanel.add(mon);
JLabel tue = new JLabel(symbolTable.getShortWeekdays()[Calendar.TUESDAY]);
tue.setFont(todayFont);
tue.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 2;
gbl.setConstraints(tue, gbc);
daysPanel.add(tue);
JLabel wed = new JLabel(symbolTable.getShortWeekdays()[Calendar.WEDNESDAY]);
wed.setFont(todayFont);
wed.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 3;
gbl.setConstraints(wed, gbc);
daysPanel.add(wed);
JLabel thu = new JLabel(symbolTable.getShortWeekdays()[Calendar.THURSDAY]);
thu.setFont(todayFont);
thu.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 4;
gbl.setConstraints(thu, gbc);
daysPanel.add(thu);
JLabel fri = new JLabel(symbolTable.getShortWeekdays()[Calendar.FRIDAY]);
fri.setFont(todayFont);
fri.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 5;
gbl.setConstraints(fri, gbc);
daysPanel.add(fri);
JLabel sat = new JLabel(symbolTable.getShortWeekdays()[Calendar.SATURDAY]);
sat.setFont(todayFont);
sat.setHorizontalAlignment(JLabel.CENTER);
gbc.gridx = 6;
gbl.setConstraints(sat, gbc);
daysPanel.add(sat);
gbc.insets = new Insets(0,2,0,2);
gbc.ipadx = 0;
for (int i=0; i<37; i++)
{
gbc.gridx = i % 7;
gbc.gridy = (i / 7) + 1;
_datebuttonArray[i] = new JdateButton(this, compact);
gbl.setConstraints(_datebuttonArray[i], gbc);
daysPanel.add(_datebuttonArray[i]);
}
/* daysPanel.setBackground(new Color(200,200,255)); // light blue*/
daysPanel.setBorder(UIManager.getBorder("TitledBorder.border"));
// and we'll wrap the daysPanel in a FlowLayout-managed
// centerPanel so that it centers itself in the mainPanel
JPanel centerPanel = new JPanel(false);
centerPanel.setLayout(new FlowLayout()); //borderlayout
centerPanel.add(daysPanel);
// centerPanel.setBackground(new Color(200,200,255)); // light blue
mainPanel.add(centerPanel, "Center");
JPanel southPanel = new JPanel();
southPanel.setLayout(new BorderLayout());
buttonPanel = new JPanel();
buttonPanel.setLayout(new BorderLayout());
// Adds close button to popup - in upper constructor
southPanel.add(buttonPanel,"South");
if (showTime)
{
timePanel = new JTimePanel(this);
if (editable)
{
// "Please choose a time of day:"
timePanel.setBorder(new TitledBorder(ts.l("init.chooseTitle")));
}
else
{
// "Time of day:"
timePanel.setBorder(new TitledBorder(ts.l("init.displayTitle")));
}
southPanel.add(timePanel,"Center");
}
mainPanel.add(southPanel,"South"); //# James new
writeDates();
setAllowMonthChange(editable);
add(mainPanel);
}
/**
* <p>This is the main programmatic entry point for setting the date selected
* in this calendar widget. Calling this method will update the selected
* time/date to that passed, and will redraw the calendar with the selected
* time/date shown.</p>
*
* <p>Calling this method will not trigger a callback to report the date
* change to our client.</p>
*
* @param date The date to be loaded
*/
public synchronized void setDate(Date date)
{
if (date == null)
{
if (debug)
{
System.err.println("JpanelCalendar.setDate(): null value");
}
dateIsSet = false;
// and re-draw
if (debug)
{
System.err.println("JpanelCalendar.setDate(): writeDates after cleared");
}
writeDates();
}
else
{
dateIsSet = true;
if (debug)
{
System.err.println("JpanelCalendar.setDate(): setting to " + date);
}
// refresh the visible calendar page (month/year) to the newly set date
visibleDate_calendar.setTime(date);
// refresh the recorded time
selectedDate_calendar.setTime(date);
if (debug)
{
System.err.println("JpanelCalendar.setDate(): calling writeDates()");
}
// do the calendar calculations to update the display
Calendar c = Calendar.getInstance();
c.setTime(date);
// refresh the month visible in the month combo box
monthYearPanel.setMonth(c.get(Calendar.MONTH));
// set the year and re-draw.. note that setYear calls writeDates()
// for us so we don't have to do that here.
setYear(c.get(Calendar.YEAR));
if (debug)
{
System.err.println("JpanelCalendar.setDate(): returned from writeDates()");
}
}
this.previousDate = date;
}
/**
* @return The date held in this calendar
*/
public Date getDate()
{
return selectedDate_calendar.getTime();
}
/**
* @return True if this calendar is configured to allow editing of
* the selected date
*/
public boolean isEditable()
{
return editable;
}
/**
* @return The month of the year currently being displayed in the
* calendar gui, in the range 0-11.
*/
public int getVisibleMonth()
{
return visibleDate_calendar.get(Calendar.MONTH);
}
/**
* @return The month of the year for the selected date, in the range
* 0-11.
*/
public int getSelectedMonth()
{
return visibleDate_calendar.get(Calendar.MONTH);
}
/**
* @return The year being displayed in the calendar gui.
*/
public int getVisibleYear()
{
return visibleDate_calendar.get(Calendar.YEAR);
}
/**
* @return The year being displayed in the calendar gui.
*/
public int getSelectedYear()
{
return selectedDate_calendar.get(Calendar.YEAR);
}
private void setYear(int year)
{
monthYearPanel.setYear(year);
writeDates();
}
public void clear()
{
setDate(null);
}
/**
* <p>This method may be used to enable or disable the month and
* year changing buttons.</p>
*
* <p>By default, the JpanelCalendar allows month flipping in
* editable calendars and does not allow it in non-editable
* calendars.</p>
*
* @param okay If true, the calendar will allow changing of the
* month and year.
*/
public void setAllowMonthChange(boolean okay)
{
allowMonthChange = okay;
monthYearPanel.setAllowMonthChange(allowMonthChange);
}
/**
* <p>This method may be used to query the calendar to see if the
* month and year changing buttons are enabled.</p>
*
* @return False if the calendar does not allow changing of the
* month/year.
*/
public boolean getAllowMonthChange()
{
return allowMonthChange;
}
/**
* <p>This will update the visibleDate_calendar according to the
* current year and month visible in the GUI controls.</p>
*
* <p>Call this from writeDates().</p>
*/
void updateDate()
{
// we actually don't care about the date, since visibleDate_calendar only
// tracks the month and year shown in the calendar
visibleDate_calendar.set(monthYearPanel.getYear(), monthYearPanel.getMonth(), 1);
}
/**
* <p>This method forces the calendar to jump to the page containing
* the selected date.</p>
*/
public void displaySelectedPage()
{
visibleDate_calendar.setTime(selectedDate_calendar.getTime());
monthYearPanel.setMonth(visibleDate_calendar.get(Calendar.MONTH));
monthYearPanel.setYear(visibleDate_calendar.get(Calendar.YEAR));
writeDates();
}
/**
* @return A string describing the month and year of the currently
* selected date.
*/
public String getSelectedMonthString()
{
return month_names[selectedDate_calendar.get(Calendar.MONTH)] + " " +
selectedDate_calendar.get(Calendar.YEAR);
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == closeButton)
{
if (debug)
{
System.err.println("Closing pCal");
}
pCal.setVisible(false);
}
else if (e.getSource() == resetButton)
{
JResetDateObject date_object = new JResetDateObject(this, selectedDate_calendar.getTime());
try
{
if (callback.setValuePerformed(date_object))
{
// if the callback didn't change the date referenced by
// the date_object to something novel, we'll just set it
// to the previousDate.
if (selectedDate_calendar.getTime().equals(date_object.getDateValue()))
{
this.setDate(previousDate);
}
// otherwise, we'll set it to whatever the callback wants.
this.setDate(date_object.getDateValue());
}
}
catch (RemoteException ex)
{
// shrug, not our problem..
}
}
}
/**
* <p>This method takes the current time held in
* selectedDate_calendar and refreshes the calendar with it.</p>
*/
public void update()
{
displaySelectedPage();
if (timePanel != null)
{
timePanel.update();
}
}
/**
* <p>This method updates the calendar buttons from the time information held
* in visibleDate_calendar.</p>
*/
protected synchronized void writeDates()
{
try
{
inhibitPainting = true;
updateDate();
// get a local copy of the calendar object indicating the month and year
// shown
GregorianCalendar temp = (GregorianCalendar) visibleDate_calendar.clone();
// find the first day of the month
temp.add(Calendar.DATE,-(temp.get(Calendar.DATE)-1));
// this is presumably here for a bug workaround
temp.setTime(temp.getTime());
int startDay = temp.get(Calendar.DAY_OF_WEEK);
for (int i = 0; i<startDay-1; i++)
{
_datebuttonArray[i].hideYourself();
}
int numDays;
if (temp.isLeapYear(temp.get(Calendar.YEAR)))
{
numDays = leapDays[temp.get(Calendar.MONTH)];
}
else
{
numDays = monthDays[temp.get(Calendar.MONTH)];
}
int day = 1;
for (int i = startDay-1; i < (startDay+numDays-1); i++,day++)
{
if (dateIsSet &&
visibleDate_calendar.get(Calendar.YEAR) == selectedDate_calendar.get(Calendar.YEAR) &&
visibleDate_calendar.get(Calendar.MONTH) == selectedDate_calendar.get(Calendar.MONTH) &&
selectedDate_calendar.get(Calendar.DATE) == day)
{
_datebuttonArray[i].setText(Integer.toString(day,10));
_datebuttonArray[i].setFont(todayFont);
_datebuttonArray[i].showYourself(Color.red);
}
else
{
_datebuttonArray[i].setText(Integer.toString(day,10));
_datebuttonArray[i].setFont(notTodayFont);
_datebuttonArray[i].showYourself(Color.black);
}
}
for (int i=startDay+numDays-1; i<37; i++)
{
_datebuttonArray[i].hideYourself();
}
if (showTime)
{
timePanel.update();
}
}
finally
{
inhibitPainting = false;
}
repaint();
}
public void update(Graphics g)
{
if (!inhibitPainting)
{
super.update(g);
}
}
/**
* <p>This method handles the final processing of any calendar button
* pushed.</p>
*
* @param _bttn The button that was pressed.
*/
public void buttonPressed(JdateButton _bttn)
{
if (debug)
{
System.err.println("buttonPressed");
}
if (!editable)
{
return;
}
if (_bttn == null)
{
throw new IllegalArgumentException("The dateButton parameter is null");
}
if (_bttn.getText().equals(""))
{
return; // no-op
}
int date = Integer.parseInt(_bttn.getText(),10);
if (debug)
{
System.err.println("setting date to day " + date);
}
visibleDate_calendar.set(Calendar.DATE,date);
visibleDate_calendar.setTime(visibleDate_calendar.getTime()); // this sets all the fields properly
selectedDate_calendar.setTime(visibleDate_calendar.getTime());
if (debug)
{
System.err.println("selectedDate_calendar = " + selectedDate_calendar.getTime());
}
// clear the button that was pressed previously
writeDates();
try
{
if (callback != null)
{
// we're going to count on our parent doing an error dialog display if
// needed.
if (!callback.setValuePerformed(new JSetValueObject(this, selectedDate_calendar.getTime())))
{
if (debug)
{
System.err.println("JpanelCalendar.buttonPressed(): oops, unacceptable date.. reverting to " +
previousDate);
}
setDate(previousDate);
if (debug)
{
System.err.println("JpanelCalendar.buttonPressed(): reverted date.");
}
}
}
}
catch (RemoteException re)
{
}
}
/**
* <p>This method is used to update the calendar's notion of time from
* information passed in from the time panel.</p>
*
* @param _field "hour", "min", or "sec"
* @param _value The hour, month, or second
*/
public void timeChanged(String _field, int _value)
{
if (!editable)
{
return;
}
if (_field == null)
{
throw new IllegalArgumentException("_field is null");
}
if (_field.equals("hour"))
{
visibleDate_calendar.set(Calendar.HOUR_OF_DAY,_value);
selectedDate_calendar.set(Calendar.HOUR_OF_DAY,_value);
}
else if (_field.equals("min"))
{
visibleDate_calendar.set(Calendar.MINUTE,_value);
selectedDate_calendar.set(Calendar.MINUTE,_value);
}
else if (_field.equals("sec"))
{
visibleDate_calendar.set(Calendar.SECOND,_value);
selectedDate_calendar.set(Calendar.SECOND,_value);
}
else
{
return;
}
try
{
if (!callback.setValuePerformed(new JSetValueObject(this, selectedDate_calendar.getTime())))
{
// constructing a JErrorDialog causes it to be shown.
// "Date Out Of Range"
// "The date you have chosen is out of the acceptable range."
new JErrorDialog(new JFrame(),
ts.l("timeChanged.dateRangeError"),
ts.l("timeChanged.dateRangeErrorText"), StandardDialog.ModalityType.DOCUMENT_MODAL);
setDate(previousDate);
}
}
catch (RemoteException re)
{
}
}
public static void main(String[] argv)
{
JFrame frame = new JFrame();
frame.getContentPane().add(new JpanelCalendar(new GregorianCalendar(), null, true, true));
frame.setSize(300,200);
frame.pack();
frame.setVisible(true);
}
}
/*------------------------------------------------------------------------------
class
JdateButton
------------------------------------------------------------------------------*/
/**
* <p>This class represents a single button in the composite {@link
* arlut.csd.JCalendar.JpanelCalendar JpanelCalendar} widget. Each
* day of the month will be represented on-screen by one of these
* buttons. If the calendar is non-editable, the buttens will be
* non-responsive when pressed, but will still be used to form the
* calendar's display.</p>
*/
class JdateButton extends JButton implements ActionListener, MouseListener {
JpanelCalendar my_parent = null;
Color
bg;
private boolean active = true;
/* -- */
public JdateButton(JpanelCalendar parent)
{
this(parent, false);
}
public JdateButton(JpanelCalendar parent, boolean compact)
{
if (parent == null)
{
throw new IllegalArgumentException("The parameter parent is null");
}
if (compact)
{
setMargin(new Insets(1,1,1,1));
setBorderPainted(false);
}
else
{
setMargin(new Insets(3,5,3,5));
}
my_parent = parent;
addActionListener(this);
addMouseListener(this);
bg = getBackground();
}
public void showYourself(Color fg)
{
if (active)
{
if (!getForeground().equals(fg))
{
setForeground(fg);
}
}
else
{
setBorderPainted(true);
setForeground(fg);
if (!getBackground().equals(bg))
{
setBackground(bg); // reset background
}
setEnabled(true);
this.active = true;
}
}
public void hideYourself()
{
if (active)
{
setText(" ");
if (isBorderPainted())
{
setBorderPainted(false);
}
if (!getForeground().equals(getBackground()))
{
setForeground(getBackground());
}
if (isEnabled())
{
setEnabled(false);
}
this.active = false;
}
}
public void actionPerformed(ActionEvent e)
{
if (my_parent == null)
{
throw new NullPointerException("dateButton: null parent");
}
if (this.active)
{
my_parent.buttonPressed(this);
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e)
{
if (isBorderPainted())
{
super.setBackground(Color.lightGray.brighter());
}
}
public void mouseExited(MouseEvent e)
{
if (isBorderPainted())
{
super.setBackground(bg);
}
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
}
/*------------------------------------------------------------------------------
class
JTimePanel
------------------------------------------------------------------------------*/
/**
* <p>This class displays the time at the bottom of the composite
* {@link arlut.csd.JCalendar.JpanelCalendar JpanelCalendar} widget.
* If the calendar widget is editable, this panel will allow the user
* to set the time corresponding with the date shown in the calendar
* widget.</p>
*
* <p>The numeric fields contained in this panel will transmit the
* change in time back to the JpanelCalendar's client whenever the
* focus exits one of the numeric fields.</p>
*/
class JTimePanel extends JPanel implements JsetValueCallback {
/**
* <p>TranslationService object for handling string localization in
* the Ganymede system.</p>
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.JCalendar.JTimePanel");
JpanelCalendar container;
JnumberField _hour = null;
JnumberField _min = null;
JnumberField _sec = null;
/* -- */
public JTimePanel(JpanelCalendar parent)
{
if (parent == null)
{
throw new IllegalArgumentException("The parameter parent is null");
}
container = parent;
_hour = new JnumberField(3,true,true,0,23,this);
_min = new JnumberField(3,true,true,0,59,this);
_sec = new JnumberField(3,true,true,0,59,this);
if (!parent.isEditable())
{
_hour.setEditable(false);
_min.setEditable(false);
_sec.setEditable(false);
}
GridLayout g = new GridLayout(1,1);
g.setHgap(0);
g.setVgap(0);
setLayout(g);
JPanel p = new JPanel();
FlowLayout fL = new FlowLayout();
fL.setHgap(0);
fL.setVgap(0);
p.setLayout(fL);
JLabel l1 = new JLabel(":",SwingConstants.CENTER);
JLabel l2 = new JLabel(":",SwingConstants.CENTER);
l1.setFont(new Font("Helvetica",Font.BOLD,14));
l2.setFont(new Font("Helvetica",Font.BOLD,14));
p.add(_hour);
p.add(l1);
p.add(_min);
p.add(l2);
p.add(_sec);
JPanel panel = new JPanel(new BorderLayout());
panel.add("West", new JLabel(ts.l("init.timeLabel"))); // "Time:"
panel.add("Center", p);
add(panel);
update();
}
/**
* <p>This method updates the numeric fields from our
* JpanelCalendar's current selected time.</p>
*/
public void update()
{
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(container.getDate());
_hour.setValue(cal.get(Calendar.HOUR_OF_DAY));
_min.setValue(cal.get(Calendar.MINUTE));
_sec.setValue(cal.get(Calendar.SECOND));
}
/**
* <p>Process callbacks from the numeric fields.</p>
*
* @param valueObj The value set.
*/
public boolean setValuePerformed(JValueObject valueObj)
{
Component comp = valueObj.getSource();
Object obj = valueObj.getValue();
if (comp == null)
{
throw new RuntimeException("comp is null");
}
if (obj == null)
{
throw new RuntimeException("obj is null");
}
if (comp != _hour && comp != _min && comp != _sec)
{
throw new RuntimeException("processEvent called from invalid component");
}
int val = ((Integer)obj).intValue();
if (comp == _hour)
{
container.timeChanged("hour",val); // "hour"
}
else if (comp == _min)
{
container.timeChanged("min",val); // "min"
}
else
{
container.timeChanged("sec",val); // "sec"
}
return true;
}
}
/*------------------------------------------------------------------------------
class
JYearChooser
------------------------------------------------------------------------------*/
/**
* <p>This class provides a 'spinner' widget for displaying and
* allowing the editing of the year in the composite {@link
* arlut.csd.JCalendar.JpanelCalendar JpanelCalendar} widget.</p>
*
* <p>JYearChooser is incorporated into JpanelCalendar by way of the
* {@link arlut.csd.JCalendar.JMonthYearPanel JMonthYearPanel}
* widget.</p>
*/
class JYearChooser extends JPanel implements ActionListener {
static final boolean debug = false;
// ---
JnumberField
year_field;
JButton
up,
down;
JMonthYearPanel
callback;
/* -- */
public JYearChooser(int year, JMonthYearPanel parent)
{
this.callback = parent;
year_field = new JnumberField(4);
year_field.setValue(year);
year_field.setEditable(true);
Image upImage = PackageResources.getImageResource(this, "up.gif", getClass());
Image downImage = PackageResources.getImageResource(this, "down.gif", getClass());
Insets my_insets = new Insets(0,0,0,0);
up = new JButton(new ImageIcon(upImage));
up.setMargin(my_insets);
up.addActionListener(this);
down = new JButton(new ImageIcon(downImage));
down.setMargin(my_insets);
down.addActionListener(this);
JPanel buttonP = new JPanel(new GridLayout(2,1));
buttonP.add(up);
buttonP.add(down);
setLayout(new BorderLayout());
add(year_field,"West");
add(buttonP,"Center");
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == up)
{
int year = year_field.getValue().intValue();
year_field.setValue(year+1);
callback.updateYear(year+1);
}
else if (e.getSource() == down)
{
int year = year_field.getValue().intValue();
year_field.setValue(year-1);
callback.updateYear(year-1);
}
}
public Integer getYear()
{
return year_field.getValue();
}
public void setYear(int year)
{
if (debug)
{
System.out.println("Setting to year: " + year);
}
year_field.setValue(year);
}
public void setAllowMonthChange(boolean doit)
{
up.setEnabled(doit);
down.setEnabled(doit);
}
}
/*------------------------------------------------------------------------------
class
JMonthYearPanel
------------------------------------------------------------------------------*/
/**
* <p>This class displays the year/month gui controls at the top of
* the composite {@link arlut.csd.JCalendar.JpanelCalendar
* JpanelCalendar} widget. If the calendar widget is editable, this
* panel will allow the user to change the month and year year
* corresponding with the date shown in the calendar widget.</p>
*/
class JMonthYearPanel extends JPanel implements ActionListener, ItemListener {
static final boolean debug = false;
/**
* <p>TranslationService object for handling string localization in
* the Ganymede system.</p>
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.JCalendar.JMonthYearPanel");
// ---
private boolean editable;
private JButton _prevdate;
private JButton _nextdate;
private JYearChooser year;
private JComboBox month;
private JpanelCalendar container;
private int currentMonth;
private int currentYear;
private JLabel mYLabel;
private Font titleFont = new Font("serif", Font.BOLD, 14);
/* -- */
public JMonthYearPanel(JpanelCalendar parent)
{
this.container = parent;
currentMonth = container.getVisibleMonth();
currentYear = container.getVisibleYear();
editable = container.getAllowMonthChange();
if (editable)
{
initializeEditable();
}
else
{
initializeNonEditable();
}
}
/**
* <p>This method sets the month in the JMonthYearPanel.</p>
*
* <p>Calling this method will update the display, but it will
* not trigger a callback to the JpanelCalendar.</p>
*
* @param index The month of year, 0 to 11.
*/
public void setMonth(int index)
{
if (index < 0 || index > 11)
{
throw new IllegalArgumentException("month out of range: " + index);
}
currentMonth = index;
if (editable)
{
month.setSelectedIndex(currentMonth);
}
else
{
mYLabel.setText(JpanelCalendar.month_names[currentMonth] + " " + currentYear);
}
}
/**
* <p>Returns the month currently selected in the JMonthYearPanel.
* This is not necessarily the same as the month of the currently
* selected day in the calendar.</p>
*
* @return The month of year chosen in this JpanelCalendar, from 0 to 11
*/
public int getMonth()
{
return currentMonth;
}
/**
* @return A human readable string containing the month shown in the
* JMonthYearPanel.
*/
public String getMonthString()
{
return JpanelCalendar.month_names[currentMonth];
}
/**
* <p>This method sets the year in the JMonthYearPanel.</p>
*
* <p>Calling this method will update the display, but it will
* not trigger a callback to the JpanelCalendar.</p>
*
* @param index The year to set in the JMonthYearPanel
*/
public void setYear(int index)
{
currentYear = index;
if (editable)
{
year.setYear(currentYear);
}
else
{
mYLabel.setText(JpanelCalendar.month_names[currentMonth] + " " + currentYear);
}
}
/**
* <p>This method passes changes from the JYearChooser up to
* the parent calendar widget.</p>
*
* @param index The year to set in the JMonthYearPanel
*/
public void updateYear(int index)
{
currentYear = index;
performCallback();
}
/**
* <p>Returns the year currently selected in the JMonthYearPanel.
* This is not necessarily the same as the year of the currently
* selected day in the calendar.</p>
*
* @return The year currently selected in the JMonthYearPanel
*/
public int getYear()
{
return currentYear;
}
/**
* <p>This method initializes or re-initializes this panel for editing.</p>
*/
public void initializeEditable()
{
this.removeAll();
setLayout(new BorderLayout());
_prevdate = new JButton("<<");
_prevdate.setToolTipText(ts.l("initializeEditable.prev_tooltip")); // "Previous Month"
_prevdate.addActionListener(this);
_nextdate = new JButton(">>");
_nextdate.setToolTipText(ts.l("initializeEditable.next_tooltip")); // "Next Month"
_nextdate.addActionListener(this);
month = new JComboBox();
month.setKeySelectionManager(new TimedKeySelectionManager());
for (int i = 0; i < JpanelCalendar.month_names.length; i++)
{
if (JpanelCalendar.month_names[i] == null || JpanelCalendar.month_names[i].equals(""))
{
// The months array returned from
// DateFormatSymbols.getMonths() returns thirteen entries
// because some calendars have thirteen (lunar) months.
// For conventional twelve month calendars, we'll have an
// empty month string at the end of our array, which we'll
// need to skip.
// NOTICE: The month buttons actions do not support this 13th month, add that in
continue;
}
month.addItem(JpanelCalendar.month_names[i]);
}
month.setSelectedIndex(currentMonth);
month.addItemListener(this);
year = new JYearChooser(currentYear, this);
JPanel middlePanel = new JPanel(new BorderLayout());
add(_prevdate, "West");
middlePanel.add("Center", month);
middlePanel.add("East", _nextdate);
add(middlePanel, "Center");
add(year, "East");
validate();
}
/**
* <p>This method initializes or re-initializes this panel for
* display.</p>
*/
public void initializeNonEditable()
{
this.removeAll();
setLayout(new BorderLayout());
mYLabel = new JLabel(JpanelCalendar.month_names[currentMonth] + " " + currentYear);
mYLabel.setFont(titleFont);
add(mYLabel, "Center");
validate();
}
/**
* <p>This method toggles this panel from display mode to editing
* mode or vice-versa.</p>
*
* @param allow If true, this panel will be editable, and the year
* and month will be changeable by the user.
*/
public void setAllowMonthChange(boolean allow)
{
if (this.editable == allow)
{
return;
}
this.editable = allow;
if (editable)
{
initializeEditable();
}
else
{
initializeNonEditable();
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == _nextdate)
{
currentMonth++;
if (currentMonth > 11)
{
currentMonth = 0;
if (debug)
{
System.out.println("Going back to Jan");
}
setYear(currentYear + 1);
}
month.setSelectedIndex(currentMonth);
performCallback();
}
else if (e.getSource() == _prevdate)
{
currentMonth--;
if (currentMonth < 0)
{
currentMonth = 11;
if (debug)
{
System.out.println("Going back to Dec");
}
setYear(currentYear - 1);
}
month.setSelectedIndex(currentMonth);
performCallback();
}
}
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
if (month.getSelectedIndex() != currentMonth)
{
int index = month.getSelectedIndex();
if (index < 0 || index > 11)
{
throw new IllegalArgumentException("month out of range: " + index);
}
currentMonth = index;
performCallback();
}
}
}
private void performCallback()
{
container.writeDates();
}
}