JTable TimeCellEditor

Gudday guys & gals,

In a JTable which is kind of a visit schedule, I need to display the start and finish times for each visit (ie, there is a colum "Start Time" and another "Finish Time"). Both columns are java.util.Date objects behind the scenes.

I'm porting this application from MS Access (yeah, yeah, I know), and the problem is that the users are currently able to enter times in "HH:mm" format. That is, they click in the table cell, and the mask displays as "__:__".

Rendering is no problem - done.

The issue that I have is in trying to get my editor working. I've Googled, and I've forumed, but turned up nothing. There's date editors - no problem (I have JCalendar working for that), but nothing I can find for time editing.

First question I suppose when taking another look at this: is there a better way to handle times when the date (ie year, month, day) is irrelevant?

If Dates are the way to go (as I suspect for later calculation of the schedule etc), I would GREATLY appreciate any direction here - I'm almost bald now!

Thanks guys,

Paul C.

[1121 byte] By [Pcasanovaa] at [2007-10-3 9:17:00]
# 1

This based on some code I found to handle specially formated fields.

It handles the rendering as well.

// declare a time field this way

final LimitedTimeField timeField = new LimitedTimeField("");

...

...

public class LimitedTimeField extends LimitedTimePatternField {

private Toolkit toolkit;

public LimitedTimeField(String value) {

super(PatternControl.getTimePattern());

toolkit = Toolkit.getDefaultToolkit();

adjustPreferredSize();

setValue(value);

setInputVerifier(new TimePassVerifier());

}

public String getFormattedValue() {

String strValue = getValue();

if (strValue.length() < 4) {return "";}

String retVal = strValue.substring(0, 2) + ":" + strValue.substring(2, 4);

return retVal;

}

protected static String removeTimeFormatting(String time){

String r1 = "";

for (int i = 0; i < time.length(); i ++){

if (time.charAt(i) != '_' && time.charAt(i) != ':'){r1 += time.charAt(i);}

}

return r1;

}

}

class TimePassVerifier extends InputVerifier{

private Toolkit toolkit;

public boolean verify(JComponent input){

toolkit = Toolkit.getDefaultToolkit();

LimitedTimeField dFld = (LimitedTimeField)input;

String strValue = LimitedTimeField.removeTimeFormatting(dFld.getText());

char[] c = strValue.toCharArray();

// If nothing has been entered just allow to leave focus

if(strValue.length() == 0) {return true;}

// Initialize the strValue variable again with time formatting in it,

strValue = dFld.getText();

if(c.length != 4) {

toolkit.beep();

return false;

}

for(int i = 0; i < c.length; i++) {

if(! Character.isDigit(c[i])){

toolkit.beep();

return false;

}

}

// Convert the time into hour, min, second set to "0" if invalid

int hour = 0;

int min = 0;

try {hour = Integer.parseInt(strValue.substring(0,3));}

catch(NumberFormatException e) {hour = 0;}

if(hour > 24) {

toolkit.beep();

return false;

}

try {min = Integer.parseInt(strValue.substring(3));}

catch(NumberFormatException e) {min = 0;}

if(min > 59) {

toolkit.beep();

return false;

}

return true;

}

}

//*********

public class LimitedTimePatternField extends JTextField implements DocumentListener, KeyListener, CaretListener {

protected LimitedTimeDocument doc;

protected int keyCode = 0;

protected int keyMods = 0;

protected boolean caretUpdateLock = false;

protected boolean dumbCaretUpdate = false;

private int KEY_CLEAR_FIELD = KeyEvent.VK_F2;

private String pattern = null;

int lenPattern = 0;

boolean ignoreKey = false;

public LimitedTimePatternField(String pat) {this(pat, LimitedTimeDocument.BLANK);}

public LimitedTimePatternField(String pat, char blk) {

doc = new LimitedTimeDocument(pat, blk);

doc.addDocumentListener(this);

addKeyListener(this);

addCaretListener(this);

setDocument(doc);

setInitialPosition();

this.pattern = pat;

this.lenPattern = pat.length();

}

public int getClearKey() {return KEY_CLEAR_FIELD;}

public void setClearKey(int keyVal) {KEY_CLEAR_FIELD = keyVal;}

public String getClearText() {return KeyEvent.getKeyText(getClearKey());}

public void adjustPreferredSize() {

if(pattern == null || lenPattern == 0) {return;}

char[] p = pattern.toCharArray();

char[] m = new char[p.length];

char[] t = doc.getBlankedPattern().toCharArray();

for(int mIndex = 0; mIndex < m.length; mIndex++) {

if(t[mIndex] == LimitedTimeDocument.BLANK) {m[mIndex] = (p[mIndex] == PatternControl.DIGIT_PATTERN) ? '8' : 'W' ;}

else {m[mIndex] = t[mIndex];}

}

String txtSave = getText();

setText(new String(m));

Dimension d = this.getPreferredSize();

Insets in = this.getInsets();

d.height += in.top + in.bottom;

d.width += in.left + in.right;

this.setPreferredSize(d);

setText(txtSave);

}

public void setText(String value) {

super.setText(value);

setInitialPosition();

}

public void setValue(String value) {setText(value);}

public String getValue() {

char[] t = getText().toCharArray();

char[] c = new char[t.length];

int cIndex = 0;

for(int tIndex = 0; tIndex < t.length; tIndex++) {

if(! doc.isFixed(tIndex)) {c[cIndex++] = t[tIndex];}

}

cIndex--;

while(cIndex >= 0) {

if(c[cIndex] != LimitedTimeDocument.BLANK) {break;}

cIndex--;

}

if(cIndex < 0) {return "";}

return (new String(c, 0, cIndex + 1)).replace(LimitedTimeDocument.BLANK, ' ');

}

private int forwardAdjustedCaret(int pos) {

if(pos < 0) {pos = 0;}

while(doc.isFixed(pos)) {pos++;}

if(pos > lenPattern) {pos = lenPattern;}

return pos;

}

private int backwardAdjustedCaret(int pos) {

if(pos < 0) {return forwardAdjustedCaret(0);}

if(pos > lenPattern) {pos = lenPattern;}

while(doc.isFixed(pos)) {pos--;}

if(pos <= 0) {return forwardAdjustedCaret(0);}

return pos;

}

private void clearField() {

setValue("");

setCaretPosition(forwardAdjustedCaret(0));

}

public void setInitialPosition() {

doc.clearLastAlteredPosition();

setCaretPosition(forwardAdjustedCaret(0));

}

public void keyTyped(KeyEvent event) {}

public void keyReleased(KeyEvent event) {}

public void keyPressed(KeyEvent event) {

if(event.isAltDown() && event.getKeyCode() != KeyEvent.VK_ALT) {ignoreKey = true;}

keyCode = event.getKeyCode();

keyMods = event.getModifiers();

int pos = getCaretPosition();

if(keyCode == KEY_CLEAR_FIELD) {

event.consume();

clearField();

}

else if(keyCode == KeyEvent.VK_BACK_SPACE) {

while(doc.isFixed(pos - 1)) {pos--;}

if(pos <= 0) {pos = forwardAdjustedCaret(0);}

dumbCaretUpdate = true;

setCaretPosition(pos);

dumbCaretUpdate = false;

}

else if(keyCode == KeyEvent.VK_LEFT) {

while(pos > lenPattern || doc.isFixed(pos - 1)) {pos--;}

if(pos <= 0) {pos = forwardAdjustedCaret(0);}

dumbCaretUpdate = true;

if((keyMods & InputEvent.SHIFT_MASK) != 0) {moveCaretPosition(pos);}

else {setCaretPosition(pos);}

dumbCaretUpdate = false;

}

}

protected void processInputMethodEvent(InputMethodEvent e) {

if(! ignoreKey) {super.processInputMethodEvent(e);}

ignoreKey = false;

}

public void changedUpdate(DocumentEvent event) {}

public void removeUpdate(DocumentEvent event) {}

public void insertUpdate(DocumentEvent event) {

if(keyCode == KeyEvent.VK_BACK_SPACE) {setCaretPosition(event.getOffset());}

else if(keyCode == KeyEvent.VK_DELETE) {setCaretPosition(event.getOffset());}

}

public void caretUpdate(CaretEvent e) {

if(doc.getLength() == 0) {return;}

if(caretUpdateLock) {return;}

caretUpdateLock = true;

if(keyCode == KeyEvent.VK_HOME) {

if((keyMods & InputEvent.SHIFT_MASK) != 0) {moveCaretPosition(forwardAdjustedCaret(0));}

else {setCaretPosition(forwardAdjustedCaret(0));}

keyCode = 0;

}

else if(keyCode == KeyEvent.VK_END) {

if((keyMods & InputEvent.SHIFT_MASK) != 0) {moveCaretPosition(backwardAdjustedCaret(lenPattern));}

else {setCaretPosition(backwardAdjustedCaret(lenPattern));}

keyCode = 0;

}

else if(keyCode == KeyEvent.VK_RIGHT) {

if((keyMods & InputEvent.SHIFT_MASK) != 0) {moveCaretPosition(forwardAdjustedCaret(e.getDot()));}

else {setCaretPosition(forwardAdjustedCaret(e.getDot()));}

keyCode = 0;

}

else if(! dumbCaretUpdate && doc.isFixed(e.getDot())) {

if(e.getDot() == e.getMark()) {setCaretPosition(forwardAdjustedCaret(e.getDot()));}

else {moveCaretPosition(forwardAdjustedCaret(e.getDot()));}

} else if(doc.getLastAlteredPosition() != -1) {

if(e.getDot() == e.getMark()) {setCaretPosition(forwardAdjustedCaret(doc.getLastAlteredPosition()));}

else {moveCaretPosition(forwardAdjustedCaret(doc.getLastAlteredPosition()));}

}

caretUpdateLock = false;

}

}

//**************

public class PatternControl {

public static final char DIGIT_PATTERN = '\u0011';

public static String getTimePattern() {

char[] pat = new char[5];

pat[0] = DIGIT_PATTERN;

pat[1] = DIGIT_PATTERN;

pat[2] = ':';

pat[3] = DIGIT_PATTERN;

pat[4] = DIGIT_PATTERN;

return new String(pat);

}

}

rykk

rykk.a at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 2
Hi Rykk,Thanks for that - just trying it out now. Any chance you have the code for LimitedTimeDocument?PaulC.
Pcasanovaa at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 3
Would anyone else have some suggestions on a time field table cell editor?Thoughts on using a JFormattedTextField?
Pcasanovaa at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 4

For example using the code I gave you the other day.

// ** your code to select specific columns and assign the the editors **

// ** for each column in the table**

public void initiateCellEditors(JTable table) {

// you must set the number of columns in your table to control the loop

// for example below I use the size of fieldInfo

// it contains one element for each column telling its type

for (int col = 0; col < fieldInfo.size();col++ ) {

....

// if this is going to be a time column

if ((Vector)fieldInfo.elemementAt(col).equals ("TIME")) {

setUpTimeEditor(table, col);

}

...

}

}

....

....

public void setUpTimeEditor(JTable table, int col) {

//Set up the editor for the time text cells.

final LimitedTimeField timeField = new LimitedTimeField("");

DefaultCellEditor timeEditor = new DefaultCellEditor(timeField) {

public Object getCellEditorValue() {

// return a string value of the time value entered

return new String(timeField.getFormattedValue());

}

};

TableColumn column = null;

column = table.getColumnModel().getColumn(col);

column.setCellEditor(timeEditor);

}

The above is just untested example code.

rykk

Message was edited by: rykk

rykk.a at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 5
Hi Rykk,Thanks for your response - any chance you'd have the code for LimitedTimeDocument, or be able to tell me it's structure? (can't compile without it).Thanks,Paul C.
Pcasanovaa at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 6
LimitedTimeDocument?The stuff I gave you gets a time field.rykk
rykk.a at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 7

Yep, understood, thank you. Maybe I'm missing something here, but LimitedTimePatternField defines a protected field of type LimitedTimeDocument, hence it won't compile.

If you could point me in the direction of the LimitedTimeDocument class it would be much appreciated.

Thanks,

Paul C.

Pcasanovaa at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 8
Hang on I must be missing somethingrykk
rykk.a at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 9

Sorry I am a very old guy and you cannot expect to much.

I have multiple editors that all use some of the various routines.

public class LimitedTimeDocument extends PlainDocument {

private Toolkit toolkit = Toolkit.getDefaultToolkit();

private String strPattern;

private String strBlankedPattern;

private char[] patt;

private int lenPattern;

private int lastPosition = -1;

public static char BLANK = '_';

public LimitedTimeDocument(String pat) {this(pat, BLANK);}

public LimitedTimeDocument(String pat, char blk) {

setPattern(pat, blk);

try {super.insertString(0, strBlankedPattern, null);}

catch(BadLocationException e) {}

}

public String getBlankedPattern() {return strBlankedPattern;}

public String getPattern() {return strPattern;}

public int getLastAlteredPosition() {return lastPosition;}

public void clearLastAlteredPosition() {lastPosition = -1;}

public void setPattern(String pat, char blk) {

BLANK = blk;

strPattern = pat;

strBlankedPattern = makeBlankedPattern();

patt = strPattern.toCharArray();

lenPattern = pat.length();

}

private String makeBlankedPattern() {

char[] rawPat = strPattern.toCharArray();

char[] blkPat = new char[rawPat.length];

for(int i = 0; i < rawPat.length; i++) {blkPat[i] = isEditablePattern(rawPat[i]) ? BLANK : rawPat[i];}

return new String(blkPat);

}

private boolean isEditablePattern(char c) {return (c == PatternControl.DIGIT_PATTERN || c == PatternControl.ALL_PATTERN || c == PatternControl.UC_ALL_PATTERN|| c == PatternControl.ALPHA_PATTERN || c == PatternControl.UC_ALPHA_PATTERN ||c == PatternControl.ALPHANUMERIC_PATTERN ||c == PatternControl.UC_ALPHANUMERIC_PATTERN ||c == PatternControl.ALPHANUMERIC_PATTERN);}

public boolean isFixed(int index) {

if(index < 0 || index >= lenPattern){return false;}

return (strBlankedPattern.charAt(index) != BLANK);

}

public void insertString(int offset, String str, AttributeSet a) throws BadLocationException {

int len = str.length();

if((offset + len) > lenPattern) {len = lenPattern - offset;}

if(offset > getLength() || offset >= lenPattern || len == 0) {return;}

char[] txtAdj = new char[lenPattern];

int idxAdj = 0;

char[] txtRaw = str.toCharArray();

int idxRaw = 0;

int idxPatt = offset;

char ch;

while(idxRaw < txtRaw.length && idxPatt < lenPattern) {

ch = patt[idxPatt];

if(isFixed(idxPatt)) {

txtAdj[idxAdj++] = ch;

idxPatt++;

}

else if(ch == PatternControl.DIGIT_PATTERN) {

if(Character.isDigit(txtRaw[idxRaw])) {

if(idxPatt==0){

if (txtRaw[idxRaw]=='0' || txtRaw[idxRaw]=='1'|| txtRaw[idxRaw]=='2') {

txtAdj[idxAdj++] = txtRaw[idxRaw];

idxPatt++;

}

else {toolkit.beep();}

}

//else if(idxPatt==1){

//if (txtRaw[idxRaw]=='0' || txtRaw[idxRaw]=='1' || txtRaw[idxRaw]=='2') {

//txtAdj[idxAdj++] = txtRaw[idxRaw];

//idxPatt++;

//}

//else {toolkit.beep();}

//}

else if(idxPatt==3){

if (txtRaw[idxRaw]<'6') {

txtAdj[idxAdj++] = txtRaw[idxRaw];

idxPatt++;

}

else {toolkit.beep();}

}

else {

txtAdj[idxAdj++] = txtRaw[idxRaw];

idxPatt++;

}

}

idxRaw++;

}

else if(ch == PatternControl.UC_ALL_PATTERN) {

txtAdj[idxAdj++] = Character.toUpperCase(txtRaw[idxRaw]);

idxPatt++;

idxRaw++;

}

else if(ch == PatternControl.UC_ALPHA_PATTERN) {

if(isAlpha(txtRaw[idxRaw])) {

txtAdj[idxAdj++] = Character.toUpperCase(txtRaw[idxRaw]);

idxPatt++;

}

idxRaw++;

}

else if(ch == PatternControl.ALPHA_PATTERN) {

if(isAlpha(txtRaw[idxRaw])) {

txtAdj[idxAdj++] = txtRaw[idxRaw];

idxPatt++;

}

idxRaw++;

}

else if(ch == PatternControl.ALL_PATTERN) {

txtAdj[idxAdj++] = txtRaw[idxRaw];

idxPatt++;

idxRaw++;

}

else if(ch == PatternControl.ALPHANUMERIC_PATTERN) {

if((isAlpha(txtRaw[idxRaw])) || (Character.isDigit(txtRaw[idxRaw]))) {

txtAdj[idxAdj++] = txtRaw[idxRaw];

idxPatt++;

}

idxRaw++;

}

else if((ch == PatternControl.UC_ALPHANUMERIC_PATTERN) || (ch == PatternControl.KEY_PATTERN)) {

if((isAlpha(txtRaw[idxRaw])) || (Character.isDigit(txtRaw[idxRaw]))) {

txtAdj[idxAdj++] = Character.toUpperCase(txtRaw[idxRaw]);

idxPatt++;

}

idxRaw++;

}

}

if(idxAdj > 0) {

super.remove(offset, idxAdj);

lastPosition = offset + idxAdj;

super.insertString(offset, (new String(txtAdj, 0, idxAdj)), a);

lastPosition = -1;

}

}

public void remove(int offs, int len) throws BadLocationException {

if((offs + len) > lenPattern) {len = lenPattern - offs;}

int lenDoc = getLength();

if(lenDoc == 0 || offs >= lenDoc || offs >= lenPattern || len <= 0) {return;}

super.remove(offs, len);

lastPosition = offs;

super.insertString(offs, strBlankedPattern.substring(offs, offs + len), null);

lastPosition = -1;

}

static boolean isAlpha(char c) {

if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {return false;}

return true;

}

}

rykk

rykk.a at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 10
Lol! I understand - and let me reiterate that I appreciate your help - thank you. I think I just need to work on my communication skills a little more.I'll give it a whirl and post the dukes if I can get it going.Thanks again.Paul C.
Pcasanovaa at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 11
Sorry, I'd been caught up on other issues with the application for ages.Your solution worked brilliantly - here's the dukes you earned!Thanks again,Paul C.
Pcasanovaa at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...
# 12

Cannot compile PatternControl.

You are calling static members that do not exist:

for example:PatternControl.ALL_PATTERN

public class PatternControl

{

public static final char DIGIT_PATTERN = '\u0011';

public static String getTimePattern ()

{

char[] pat = new char[5];

pat[0] = DIGIT_PATTERN;

pat[1] = DIGIT_PATTERN;

pat[2] = ':';

pat[3] = DIGIT_PATTERN;

pat[4] = DIGIT_PATTERN;

return new String(pat);

}

}

thanks

Dekel

dekely@amdocs.coma at 2007-7-15 4:29:57 > top of Java-index,Desktop,Core GUI APIs...