2007/100 13:12:34 is the time string. I move mouse in front the 1 in
the minute field and type 3. What I want is to overwrite the 1 with
the 3 and reset the cursor right after the new 3 character to get
2007/100 13:32:34, but instead the I get 2007/100 13:332:34 with the
cursor after the second 3. The problem appears to be that the
e.consume() call is not working giving me a double 33.
Googling, I saw there were a bug reports/comments on this type of
problem but according to sun it was fix by java 1.4 (I compiled at 1.5
and 1.6 without any change). I must be doing something wrong, but
what?
Java code as follows:
Main.java
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package timespinner;
import java.awt.event.ActionEvent;
import javax.swing.*;
import java.awt.*;
import java.util.*;
public class Main {
private JButton SaveButton = new JButton("print");
private myDateTimeSpinner dateTimeSpinner = null;
public static void main(String[] args) {
Main h = new Main();
}
public Main() {
JFrame frame = new JFrame("Creating JSpinner Component with
time");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
dateTimeSpinner = new myDateTimeSpinner();
frame.add(dateTimeSpinner, BorderLayout.NORTH);
SaveButton.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
SaveButton_actionPerformed(e);
}
});
frame.add(SaveButton, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
private void SaveButton_actionPerformed(ActionEvent e) {
String timeStr = dateTimeSpinner.getDateString();
System.out.println(timeStr);
}
}
myDateTimeSpinner.java
package timespinner;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
public class myDateTimeSpinner extends JPanel {
private Date date = null;
private JSpinner spinner = null;
JSpinner.DateEditor dateEditor = null;
private SpinnerDateModel sm = null;
// Our Constants that I like the other people being able to see
public static final int YEAR_1 = 0;
public static final int YEAR_2 = 1;
public static final int YEAR_3 = 2;
public static final int YEAR_4 = 3; // (note: this is the 4th
char)
public static final int SL_1 = 4;
public static final int DAY_1 = 5;
public static final int DAY_2 = 6;
public static final int DAY_3 = 7; // (note: this is the 8th
char)
public static final int SP_1 = 8;
public static final int HR_1 = 9;
public static final int HR_2 = 10; // (note: this is the 11th
char)
public static final int CO_1 = 11;
public static final int MM_1 = 12;
public static final int MM_2 = 13; // (note: this is the 14th
char)
public static final int CO_2 = 14;
public static final int SS_1 = 15;
public static final int SS_2 = 16; // (note: this is the 17th
char)
private static final int BASE_LEAP_YEAR = 1972; // This number
gives us the base year (since 1972 had a 29th day of Feb)
public myDateTimeSpinner() {
date = new Date();
jbInit();
}
public myDateTimeSpinner(Date myDate) {
date = myDate;
jbInit();
}
public long getDate() {
return date.getTime();
}
public String getDateString() {
return dateEditor.getTextField().getText();
}
public void jbInit() {
sm = new SpinnerDateModel(date, null, null,
Calendar.HOUR_OF_DAY);
spinner = new JSpinner(sm);
dateEditor = new JSpinner.DateEditor(spinner, "yyyy/DDD
HH:mm:ss");
spinner.setEditor(dateEditor);
dateEditor.getTextField().addKeyListener(new
java.awt.event.KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
keyPressed_actionPerformed(e);
}
});
add(spinner);
}
private void keyPressed_actionPerformed(KeyEvent e) {
// Let's just allow only Arrow keys and nothing else
// Trap the keys that we allow them to have KEY_TYPED
int intKeyCode = e.getKeyCode();
switch (intKeyCode) {
// Let them do numbers
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
checkString(intKeyCode);
e.consume();
break;
// Let them do number pad as well
case KeyEvent.VK_NUMPAD0:
case KeyEvent.VK_NUMPAD1:
case KeyEvent.VK_NUMPAD2:
case KeyEvent.VK_NUMPAD3:
case KeyEvent.VK_NUMPAD4:
case KeyEvent.VK_NUMPAD5:
case KeyEvent.VK_NUMPAD6:
case KeyEvent.VK_NUMPAD7:
case KeyEvent.VK_NUMPAD8:
case KeyEvent.VK_NUMPAD9:
// Need to convert them back to key code
// (Use the magic number!!)
intKeyCode = intKeyCode - 48;
checkString(intKeyCode);
e.consume();
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
// Let them go left or right
nextPosition(intKeyCode, cursorAt());
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_UP:
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_TAB:
break;
default:
break;
}
}
public int cursorAt() {
// Always returns the LEFT MOST position of the selected chars
return dateEditor.getTextField().getSelectionStart();
}
/**
* This basically tells us if the use is on the right spot
* @param intKeyCode The key that was pressed
* @param intCharPos The position it was in
*/
//
-------------------------------------------------------------------------
public void nextPosition(int intKeyCode, int intCharPos) {
if (intKeyCode == KeyEvent.VK_LEFT) {
// Do the left key case
switch (intCharPos) {
case SS_1:
intCharPos = MM_2 + 1;
break;
case MM_1:
intCharPos = HR_2 + 1;
break;
case HR_1:
intCharPos = DAY_3 + 1;
break;
case DAY_1:
intCharPos = YEAR_4 + 1;
break;
default:
break;
}
// Reposition the Cursor (But advance it one char
setCursorPos(intCharPos);
} else if (intKeyCode == KeyEvent.VK_RIGHT) {
// Do the right key case
switch (intCharPos) {
case YEAR_4:
intCharPos = DAY_1 - 1;
break;
case DAY_3:
intCharPos = HR_1 - 1;
break;
// All the " day "
case HR_2:
intCharPos = MM_1 - 1;
break;
case MM_2:
intCharPos = SS_1 - 1;
break;
default:
break;
}
// Reposition the Cursor (But advance it one char
setCursorPos(intCharPos);
} else {
// Do nothing
}
}
/**
* Checks to see if the <code>String</code> just entered is
correct
* (Note: this one allows "invalid" strings)
* If it is correct, the appropriate data storage and GUI elements
are
* updated.
*
* @param intKeyCode The key that was pressed, so we can either
reject/accept.
*
* <P>
* author John Kerich<br>
* version 2.0, IDR 225 04/12/2006 Add class:member and verbose
level to printLogMsg.
*/
public void checkString(int intKeyCode) {
// The goal of this procedure is to allow user to type
// in ANY "String" (Well, close to any) so they won't
// be stopped when they want to continue on
// Therefore, we don't even need a UtDate or UtDuration object
String strTempNewValue;
// This is where the last char was changed
// Since everytime a key is press the cursor advances
int intCharPos = cursorAt();
// 1. Check to see if it's even worth consider
if (isReplaceable(intCharPos)) {
// 2. Get the string we need
strTempNewValue = replaceIndex(intCharPos,
KeyEvent.getKeyText(intKeyCode));
// 3. Call the User Logic Object with this value
Date d = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/DDD
HH:mm:ss");
d = sdf.parse(strTempNewValue + " GMT");
} catch (Exception e) {
return;
}
// we're decide how many space to skip
date = d;
dateEditor.getTextField().setText(strTempNewValue);
switch (intCharPos) {
// According to this: 1998/240 14:14:45
case YEAR_4:
case DAY_3:
case HR_2:
case MM_2:
intCharPos = intCharPos + 2;
break;
default:
// By default, don't let them type anything
intCharPos = intCharPos + 1;
break;
}
setCursorPos(intCharPos);
dateEditor.getTextField().repaint();
/*
try {
spinner.commitEdit();
} catch (ParseException ex) {
Logger.getLogger(myDateTimeSpinner.class.getName()).log(Level.SEVERE,
null, ex);
}
*/
}
}
/**
* Set the position of the cursor on the text field
* @param intCurPos The interger position of the text field
*/
//
-------------------------------------------------------------------------
public void setCursorPos(int intCurPos) {
dateEditor.getTextField().setSelectionStart(intCurPos);
dateEditor.getTextField().setSelectionEnd(intCurPos);
}
/**
* This basically tells us if the use is on the right spot
*
* @return true - valid or false - invalid
*
* @param intKeyCode The key that was pressed
* @param intCharPos The position
*/
//
-------------------------------------------------------------------------
public boolean isReplaceable(int intCharPos) {
boolean bResult = false;
// Check the position of the cursor
switch (intCharPos) {
// Let them do numbers
case YEAR_1:
case YEAR_2:
case YEAR_3:
case YEAR_4:
case DAY_1:
case DAY_2:
case DAY_3:
case HR_1:
case HR_2:
case MM_1:
case MM_2:
case SS_1:
case SS_2:
bResult = true;
break;
default:
bResult = false;
break;
}
return bResult;
}
/**
* Given a String (array of char) and an arbitrary char,
* replace the given index spot with the
* given char in 2nd argu
* @param strOriginal The Original string
* @param intIndex The index position to replace the
original String
* @param strSomeString The new charactor we're about to
intruduce
* @return The New String that has been replaced
*/
//
-------------------------------------------------------------------------
public String replaceIndex(int intIndex, String strSomeString) {
String strResult = new String();
String strHead = new String();
String strTail = new String();
String strOriginal = dateEditor.getTextField().getText();
int intOriginalLength = strOriginal.length();
// Check for Index bounds
if (intIndex < intOriginalLength) {
if (intIndex == 0) {
// Just replace the first char (since "zero" is a
special case)
strResult =
strSomeString.concat(strOriginal.substring(1));
} else {
// Get the beginning of the subString
strHead = strOriginal.substring(0, intIndex);
// Then concat the rest
strTail =
strOriginal.substring(intIndex + 1);
strResult =
strHead + strSomeString + strTail;
}
} else {
strResult = strOriginal;
}
return strResult;
}
//
---------------------------------------------------------------------------
// Tells us if this is a leap year
public boolean isLeapYear(int intSomeYear) {
boolean bResult = false;
int intDifference = intSomeYear - BASE_LEAP_YEAR;
// Check to see if they're four year apart
if (intDifference % 4 == 0) {
bResult = true;
}
return bResult;
}
}
Please do not multi-post.
--
Lew
myDateTimeSpinner.java
package timespinner;
import java.awt.event.KeyEvent;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
public class myDateTimeSpinner extends JPanel {
private Date date = null;
private JSpinner spinner = null;
JSpinner.DateEditor dateEditor = null;
private SpinnerDateModel sm = null;
boolean consumeKey = false; // scyncronize the consume calls for
the three types of keyevent actions
consumeKey = keyPressed_actionPerformed(e);
if (consumeKey) {
e.consume();
}
}
@Override
public void keyTyped(KeyEvent ke) {
if (consumeKey) {
ke.consume();
}
}
@Override
public void keyReleased(KeyEvent ke) {
if (consumeKey) {
ke.consume();
}
consumeKey = false;
}
});
add(spinner);
}
private boolean keyPressed_actionPerformed(KeyEvent e) {
// Let's just allow only Arrow keys and nothing else
// Trap the keys that we allow them to have KEY_TYPED
boolean consume = false;
int intKeyCode = e.getKeyCode();
switch (intKeyCode) {
// Let them do numbers
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
checkString(intKeyCode);
consume = true;
break;
// Let them do number pad as well
case KeyEvent.VK_NUMPAD0:
case KeyEvent.VK_NUMPAD1:
case KeyEvent.VK_NUMPAD2:
case KeyEvent.VK_NUMPAD3:
case KeyEvent.VK_NUMPAD4:
case KeyEvent.VK_NUMPAD5:
case KeyEvent.VK_NUMPAD6:
case KeyEvent.VK_NUMPAD7:
case KeyEvent.VK_NUMPAD8:
case KeyEvent.VK_NUMPAD9:
// Need to convert them back to key code
// (Use the magic number!!)
intKeyCode = intKeyCode - 48;
checkString(intKeyCode);
consume = true;
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
// Let them go left or right
nextPosition(intKeyCode, cursorAt());
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_UP:
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_TAB:
break;
default:
break;
}
return consume;
}
String strTempNewValue;
String strOriginal = dateEditor.getTextField().getText();
strTempNewValue = replaceIndex(intCharPos,
KeyEvent.getKeyText(intKeyCode));
if (!isValidString(strTempNewValue)) {
dateEditor.getTextField().setText(strOriginal);
setCursorPos(intCharPos);
return;
}
// 3. Call the User Logic Object with this value
Date d = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/DDD
HH:mm:ss");
d = sdf.parse(strTempNewValue);
} catch (ParseException e) {
dateEditor.getTextField().setText(strOriginal);
setCursorPos(intCharPos);
return;
}
// we're decide how many space to skip
date = d;
dateEditor.getTextField().setText(strTempNewValue);
switch (intCharPos) {
// According to this: 1998/240 14:14:45
case YEAR_4:
case DAY_3:
case HR_2:
case MM_2:
intCharPos = intCharPos + 2;
break;
default:
// By default, don't let them type anything
intCharPos = intCharPos + 1;
break;
}
setCursorPos(intCharPos);
}
}
/**
return bResult;
}
} else {
strResult = strOriginal;
}
return strResult;
}
/**
* This code is to decouple the reliance on "UtDate"
* The story is, it (UtDate) used to able set a "date" with a
String
* e.g. 1998/240 25:00:15 will become 1998/241 02:00:15
* this "implicit" (automatic) rolling FEATURE as of Oct 9, 1998
* So therefore, I've decided NOT to rely on such and use
* "isValidString" to validate user input
* Date Format 1998/240 14:14:45
*
* @return true - Date was valid or false - Date was invalid
*
* @param strSomeDate The <code>String</code> that is going to
be displayed
*
*/
public boolean isValidString(String strSomeDate) {
boolean bResult = false;
boolean bGoodDay = false;
boolean bGoodHour = false;
boolean bGoodMinute = false;
boolean bGoodSecond = false;
int intDaysOfaYear = 365;
// Get our current Date string's Year part
Integer intDaysInThisYear =
Integer.valueOf(strSomeDate.substring(YEAR_1, YEAR_4 + 1));
// Tells us if this current year is a leap year
if (isLeapYear(intDaysInThisYear.intValue())) {
// This way we can have 366 in our leap years
intDaysOfaYear = 366;
}
// Figure out what our Day value is
Integer intSomeDay =
Integer.valueOf(strSomeDate.substring(DAY_1, DAY_3 + 1));
if ((intSomeDay.intValue() < intDaysOfaYear) &&
(intSomeDay.intValue() > 0)) {
bGoodDay = true;
}
// Figure out what our Hour value is
Integer intSomeHour =
Integer.valueOf(strSomeDate.substring(HR_1, HR_2 + 1));
if (intSomeHour.intValue() < 24) {
bGoodHour = true;
}
// Figure out what our Minute value is
Integer intSomeMinute =
Integer.valueOf(strSomeDate.substring(MM_1, MM_2 + 1));
if (intSomeMinute.intValue() < 60) {
bGoodMinute = true;
}
// Figure out what our Second value is
Integer intSomeSecond =
Integer.valueOf(strSomeDate.substring(SS_1, SS_2 + 1));
if (intSomeSecond.intValue() < 60) {
bGoodSecond = true;
}
// Finally decide our return value
if (bGoodDay && bGoodHour && bGoodMinute && bGoodSecond) {