/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* DateTimeValueEntryField.java
* Creation date: (09/03/01 10:28:18 AM)
* By: Michael Cheng
*/
/*
* Based on the DateTimeEditor created by David M. Karr of Best Consulting and TCSI Corporation.
* The DateTimeEditor is provided as sample code in the book �Swing� by Matthew Robinson and
* Pavel Vorobiev. The books's website (which includes this sample code) is at http://www.manning.com/sbe/
*
* No specific license statement appears to be present anywhere in the book or on the website.
* However a comment from Matt Robinson on the forum dated Nov 14, 2002 appears to indicate that this code
* is freely-available, with the condition that the book be recommended if found useful.
* We certainly found it useful for this class.
*
* The following is from http://www.manning-sandbox.com/thread.jspa?messageID=9314③
*
*
* source code availability
* Posted: Oct 20, 2002 11:00 PM
* [Originally posted by dan_can]
*
* Is the code used in the Swing book covered under a General Public License and
* available for use in non-commercial projects?
*
*
* Re: source code availability
* Posted: Oct 22, 2002 11:00 PM
* [Originally posted by pavelv]
*
* Yes, it's available.
*
* Pavel
*
*
* Re: source code availability
* Posted: Nov 11, 2002 11:00 PM
* [Originally posted by me1dif]
*
* > Yes, it's available.
* >
* > Pavel
* Please can you clarify, is the code available under the GNU license, or some
* other terms? I want to check this carefully before using it in my work.
*
* Thanks,
*
* David.
*
*
* Re: source code availability
* Posted: Nov 14, 2002 11:00 PM
* [Originally posted by matt]
*
* David, pls feel free to use the code as you see fit - the only thing we ask is
* that you recommend our book to others if you find it useful
*
* regards,
* -Matt
*/
package org.openquark.gems.client.valueentry;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.text.FieldPosition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.NoSuchElementException;
import javax.swing.AbstractAction;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.Caret;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;
import javax.swing.text.Keymap;
import javax.swing.text.TextAction;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.module.Cal.Utilities.CAL_RelativeTime;
import org.openquark.cal.module.Cal.Utilities.CAL_Time;
import org.openquark.cal.valuenode.RelativeTemporalValueNode;
import org.openquark.cal.valuenode.ValueNode;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.TimeZone;
/**
* Special ValueEntryField used when handling Date or Time or DateTime data types.
* Current applications are for RelativeTime.RelativeDate. RelativeTime.RelativeTime, RelativeTime.RelativeDateTime and Time.Time.
*
* Based on the DateTimeEditor created by David M. Karr of Best Consulting and TCSI Corporation.
* The DateTimeEditor is provided as sample code in the book �Swing� by Matthew Robinson and
* Pavel Vorobiev. The books's website (which includes this sample code) is at http://www.manning.com/sbe/
*
* Creation date: (09/03/01 10:28:18 AM)
*
* @author Michael Cheng
*/
class DateTimeValueEntryField extends ValueEntryField {
private static final long serialVersionUID = -1376225814257856569L;
public static final long ONE_SECOND = 1000;
public static final long ONE_MINUTE = 60 * ONE_SECOND;
public static final long ONE_HOUR = 60 * ONE_MINUTE;
public static final long ONE_DAY = 24 * ONE_HOUR;
public static final long ONE_WEEK = 7 * ONE_DAY;
//constants for the different types handled by this entry field.
public final static int RELATIVETIME = 0;
public final static int RELATIVEDATE = 1;
public final static int RELATIVEDATETIME = 2;
public final static int JTIME = 3;
private int m_timeOrDateType;
private DateFormat m_format;
private Calendar m_calendar;
private final ArrayList<FieldPosition> m_fieldPositions;
private Date m_lastDate;
private Caret m_caret;
private int m_curField = -1;
private JTextField m_textField;
private final AbstractAction m_upAction = new UpDownAction(1, "up");
private final AbstractAction m_downAction = new UpDownAction(-1, "down");
private final int[] m_fieldTypes =
{
DateFormat.ERA_FIELD,
DateFormat.YEAR_FIELD,
DateFormat.MONTH_FIELD,
DateFormat.DATE_FIELD,
DateFormat.HOUR_OF_DAY1_FIELD,
DateFormat.HOUR_OF_DAY0_FIELD,
DateFormat.MINUTE_FIELD,
DateFormat.SECOND_FIELD,
DateFormat.MILLISECOND_FIELD,
DateFormat.DAY_OF_WEEK_FIELD,
DateFormat.DAY_OF_YEAR_FIELD,
DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD,
DateFormat.WEEK_OF_YEAR_FIELD,
DateFormat.WEEK_OF_MONTH_FIELD,
DateFormat.TIMEZONE_FIELD,
DateFormat.AM_PM_FIELD,
DateFormat.HOUR1_FIELD,
DateFormat.HOUR0_FIELD };
protected class UpDownAction extends AbstractAction {
private static final long serialVersionUID = 5245800495509559943L;
int m_direction; // +1 = up; -1 = down
public UpDownAction(int direction, String name) {
super(name);
m_direction = direction;
}
public void actionPerformed(ActionEvent evt) {
if (!this.isEnabled()) {
return;
}
boolean dateSet = true;
switch (m_curField) {
case DateFormat.AM_PM_FIELD :
m_lastDate.setTime(m_lastDate.getTime() + (m_direction * 12 * ONE_HOUR));
break;
case DateFormat.DATE_FIELD :
case DateFormat.DAY_OF_WEEK_FIELD :
case DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD :
case DateFormat.DAY_OF_YEAR_FIELD :
m_lastDate.setTime(m_lastDate.getTime() + (m_direction * ONE_DAY));
break;
case DateFormat.ERA_FIELD :
case DateFormat.TIMEZONE_FIELD :
dateSet = false;
break;
case DateFormat.HOUR0_FIELD :
case DateFormat.HOUR1_FIELD :
case DateFormat.HOUR_OF_DAY0_FIELD :
case DateFormat.HOUR_OF_DAY1_FIELD :
m_lastDate.setTime(m_lastDate.getTime() + (m_direction * ONE_HOUR));
break;
case DateFormat.MILLISECOND_FIELD :
m_lastDate.setTime(m_lastDate.getTime() + (m_direction * 1));
break;
case DateFormat.MINUTE_FIELD :
m_lastDate.setTime(m_lastDate.getTime() + (m_direction * ONE_MINUTE));
break;
case DateFormat.MONTH_FIELD :
m_calendar.set(Calendar.MONTH, m_calendar.get(Calendar.MONTH) + m_direction);
m_lastDate = m_calendar.getTime();
break;
case DateFormat.SECOND_FIELD :
m_lastDate.setTime(m_lastDate.getTime() + (m_direction * ONE_SECOND));
break;
case DateFormat.WEEK_OF_MONTH_FIELD :
m_calendar.set(Calendar.WEEK_OF_MONTH, m_calendar.get(Calendar.WEEK_OF_MONTH) + m_direction);
m_lastDate = m_calendar.getTime();
break;
case DateFormat.WEEK_OF_YEAR_FIELD :
m_calendar.set(Calendar.WEEK_OF_MONTH, m_calendar.get(Calendar.WEEK_OF_MONTH) + m_direction);
m_lastDate = m_calendar.getTime();
break;
case DateFormat.YEAR_FIELD :
m_calendar.set(Calendar.YEAR, m_calendar.get(Calendar.YEAR) + m_direction);
m_lastDate = m_calendar.getTime();
break;
default :
dateSet = false;
}
if (dateSet) {
int fieldId = m_curField;
setDate(m_lastDate);
FieldPosition fieldPosition = getFieldPosition(fieldId);
m_caret.setDot(fieldPosition.getBeginIndex());
m_textField.requestFocus();
repaint();
}
}
}
protected class BackwardAction extends TextAction {
private static final long serialVersionUID = -2108060287476804972L;
BackwardAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
int dot = target.getCaretPosition();
if (dot > 0) {
FieldPosition position = getPrevField(dot);
if (position != null) {
target.setCaretPosition(position.getBeginIndex());
} else {
position = getFirstField();
if (position != null) {
target.setCaretPosition(position.getBeginIndex());
}
}
} else {
target.getToolkit().beep();
}
target.getCaret().setMagicCaretPosition(null);
}
}
}
protected class ForwardAction extends TextAction {
private static final long serialVersionUID = 6714055439023582206L;
ForwardAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
FieldPosition position = getNextField(target.getCaretPosition());
if (position != null) {
target.setCaretPosition(position.getBeginIndex());
} else {
position = getLastField();
if (position != null) {
target.setCaretPosition(position.getBeginIndex());
}
}
target.getCaret().setMagicCaretPosition(null);
}
}
}
protected class BeginAction extends TextAction {
private static final long serialVersionUID = 130846739211334762L;
BeginAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
FieldPosition position = getFirstField();
if (position != null) {
target.setCaretPosition(position.getBeginIndex());
}
}
}
}
protected class EndAction extends TextAction {
private static final long serialVersionUID = 4181056171567621736L;
EndAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
FieldPosition position = getLastField();
if (position != null) {
target.setCaretPosition(position.getBeginIndex());
}
}
}
}
/**
* DateTimeValueEntryField constructor.
* The type (Date/Time/DateTime) will depend on the valueNode of the ValueEntryPanel passed as an argument.
* @param valueEntryPanel The ValueEntryPanel which uses this ValueEntryField.
*/
public DateTimeValueEntryField(ValueEntryPanel valueEntryPanel) {
super(valueEntryPanel);
ValueNode valueNode = valueEntryPanel.getValueNode();
TypeExpr typeExpr = valueNode.getTypeExpr();
if (typeExpr.isNonParametricType(CAL_RelativeTime.TypeConstructors.RelativeTime)) {
m_timeOrDateType = RELATIVETIME;
} else if (typeExpr.isNonParametricType(CAL_RelativeTime.TypeConstructors.RelativeDate)) {
m_timeOrDateType = RELATIVEDATE;
} else if (typeExpr.isNonParametricType(CAL_RelativeTime.TypeConstructors.RelativeDateTime)) {
m_timeOrDateType = RELATIVEDATETIME;
} else if(typeExpr.isNonParametricType(CAL_Time.TypeConstructors.Time)) {
m_timeOrDateType = JTIME;
} else {
throw new IllegalArgumentException("Error in constructor DateTimeValueEntryField:\nThe param valueNode is not of type Date/Time/DateTime.");
}
if(m_timeOrDateType == JTIME) {
m_calendar = Calendar.getInstance(TimeZone.getDefault());
} else {
m_calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
}
m_fieldPositions = new ArrayList<FieldPosition>();
m_lastDate = new Date();
init();
}
public Date getDate() {
return (m_lastDate);
}
private FieldPosition getField(int caretLoc) {
FieldPosition fieldPosition = null;
for (final FieldPosition chkFieldPosition : m_fieldPositions) {
if ((chkFieldPosition.getBeginIndex() <= caretLoc) && (chkFieldPosition.getEndIndex() > caretLoc)) {
fieldPosition = chkFieldPosition;
break;
}
}
return (fieldPosition);
}
private FieldPosition getFieldPosition(int fieldNum) {
FieldPosition result = null;
for (final FieldPosition fieldPosition : m_fieldPositions) {
if (fieldPosition.getField() == fieldNum) {
result = fieldPosition;
break;
}
}
return (result);
}
private void getFieldPositions() {
m_fieldPositions.clear();
for (int ctr = 0; ctr < m_fieldTypes.length; ++ctr) {
int fieldId = m_fieldTypes[ctr];
FieldPosition fieldPosition = new FieldPosition(fieldId);
StringBuffer formattedField = new StringBuffer();
m_format.format(m_lastDate, formattedField, fieldPosition);
if (fieldPosition.getEndIndex() > 0) {
m_fieldPositions.add(fieldPosition);
}
}
m_fieldPositions.trimToSize();
Collections.sort(m_fieldPositions, new Comparator<FieldPosition>() {
public int compare(FieldPosition o1, FieldPosition o2) {
return ((o1).getBeginIndex() - (o2).getBeginIndex());
}
});
}
private FieldPosition getFirstField() {
FieldPosition result = null;
try {
result = m_fieldPositions.get(0);
} catch (NoSuchElementException ex) {
}
return (result);
}
private FieldPosition getLastField() {
FieldPosition result = null;
try {
result = m_fieldPositions.get(m_fieldPositions.size() - 1);
} catch (NoSuchElementException ex) {
}
return (result);
}
private FieldPosition getNextField(int caretLoc) {
FieldPosition fieldPosition = null;
for (final FieldPosition chkFieldPosition : m_fieldPositions) {
if (chkFieldPosition.getBeginIndex() > caretLoc) {
fieldPosition = chkFieldPosition;
break;
}
}
return (fieldPosition);
}
private FieldPosition getPrevField(int caretLoc) {
FieldPosition fieldPosition = null;
for (int ctr = m_fieldPositions.size() - 1; ctr > -1; --ctr) {
FieldPosition chkFieldPosition = m_fieldPositions.get(ctr);
if (chkFieldPosition.getEndIndex() <= caretLoc) {
fieldPosition = chkFieldPosition;
break;
}
}
return (fieldPosition);
}
public int getTimeOrDateType() {
return m_timeOrDateType;
}
private void init() {
m_textField = this;
m_caret = m_textField.getCaret();
m_caret.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent evt) {
setCurField();
}
});
setupKeymap();
}
private void setCurField() {
FieldPosition fieldPosition = getField(m_caret.getDot());
if (fieldPosition != null) {
if (m_caret.getDot() != fieldPosition.getBeginIndex()) {
m_caret.setDot(fieldPosition.getBeginIndex());
}
} else {
fieldPosition = getPrevField(m_caret.getDot());
if (fieldPosition != null) {
m_caret.setDot(fieldPosition.getBeginIndex());
} else {
fieldPosition = getFirstField();
if (fieldPosition != null) {
m_caret.setDot(fieldPosition.getBeginIndex());
}
}
}
if (fieldPosition != null) {
m_curField = fieldPosition.getField();
} else {
m_curField = -1;
}
}
/**
* Indicates the type of the underlying date. The display format will be
* set to the corresponding style.
* @param formatCode One of DateTimeValueEntryField.RELATIVEDATE, DateTimeValueEntryField.RELATIVETIME,
* DateTimeValueEntryField.RELATIVEDATETIME, or DateTimeValueEntryField.JTIME.
*/
public void setFormat(int formatCode) {
// Short-circuit return for unchanged format
if(formatCode==m_timeOrDateType) {
return;
}
switch(formatCode) {
case RELATIVEDATE:
case RELATIVETIME:
case RELATIVEDATETIME:
m_timeOrDateType = formatCode;
m_calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
this.setDate(m_lastDate);
break;
case JTIME:
m_timeOrDateType = formatCode;
m_calendar = Calendar.getInstance(TimeZone.getDefault());
this.setDate(m_lastDate);
break;
default:
throw new IllegalArgumentException("Illegal format code "+formatCode+ " passed to DateTimeValueEntryField.setFormat");
}
}
public void setDate(Date date) {
setupFormat();
getFieldPositions();
m_lastDate = date;
m_calendar.setTime(m_lastDate);
m_textField.setText(m_format.format(m_lastDate));
m_caret.setDot(0);
setCurField();
repaint();
}
protected void setupFormat() {
switch (m_timeOrDateType) {
case RELATIVETIME :
m_format = RelativeTemporalValueNode.getDateFormat(-1, DateFormat.MEDIUM);
break;
case RELATIVEDATE :
m_format = RelativeTemporalValueNode.getDateFormat(DateFormat.FULL, -1);
break;
case RELATIVEDATETIME :
m_format = RelativeTemporalValueNode.getDateFormat(DateFormat.FULL, DateFormat.MEDIUM);
break;
case JTIME :
m_format = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG);
m_format.setTimeZone(TimeZone.getDefault());
break;
}
}
protected void setupKeymap() {
Keymap keymap = JTextComponent.addKeymap("DateTimeKeymap", null);
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), m_upAction);
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), m_downAction);
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new BackwardAction(DefaultEditorKit.backwardAction));
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new ForwardAction(DefaultEditorKit.forwardAction));
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), new BeginAction(DefaultEditorKit.beginAction));
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), new EndAction(DefaultEditorKit.endAction));
m_textField.setKeymap(keymap);
}
}