001: package net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent;
002:
003: /*
004: * Copyright (C) 2001-2004 Colin Bell
005: * colbell@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.awt.event.*;
022: import java.awt.GridBagLayout;
023: import java.awt.GridBagConstraints;
024: import javax.swing.event.ChangeListener;
025: import javax.swing.event.ChangeEvent;
026: import java.awt.Insets;
027:
028: import java.io.FileInputStream;
029: import java.io.FileOutputStream;
030: import java.io.InputStreamReader;
031: import java.io.OutputStreamWriter;
032: import java.io.IOException;
033:
034: import javax.swing.JTable;
035: import javax.swing.JTextField;
036: import javax.swing.JTextArea;
037: import javax.swing.SwingUtilities;
038: import javax.swing.JCheckBox;
039: import javax.swing.JComboBox;
040: import javax.swing.BorderFactory;
041:
042: import javax.swing.text.JTextComponent;
043: import java.sql.PreparedStatement;
044: import java.sql.ResultSet;
045: import java.sql.Time;
046:
047: import java.text.DateFormat;
048:
049: import net.sourceforge.squirrel_sql.fw.datasetviewer.CellDataPopup;
050: import net.sourceforge.squirrel_sql.fw.datasetviewer.ColumnDisplayDefinition;
051: import net.sourceforge.squirrel_sql.fw.gui.OkJPanel;
052: import net.sourceforge.squirrel_sql.fw.gui.RightLabel;
053: import net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData;
054: import net.sourceforge.squirrel_sql.fw.util.StringManager;
055: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
056: import net.sourceforge.squirrel_sql.fw.util.ThreadSafeDateFormat;
057: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
058: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
059:
060: /**
061: * @author gwg
062: *
063: * This class provides the display components for handling Time data types,
064: * specifically SQL type TIME.
065: * The display components are for:
066: * <UL>
067: * <LI> read-only display within a table cell
068: * <LI> editing within a table cell
069: * <LI> read-only or editing display within a separate window
070: * </UL>
071: * The class also contains
072: * <UL>
073: * <LI> a function to compare two display values
074: * to see if they are equal. This is needed because the display format
075: * may not be the same as the internal format, and all internal object
076: * types may not provide an appropriate equals() function.
077: * <LI> a function to return a printable text form of the cell contents,
078: * which is used in the text version of the table.
079: * </UL>
080: * <P>
081: * The components returned from this class extend RestorableJTextField
082: * and RestorableJTextArea for use in editing table cells that
083: * contain values of this data type. It provides the special behavior for null
084: * handling and resetting the cell to the original value.
085: */
086:
087: public class DataTypeTime extends BaseDataTypeComponent implements
088: IDataTypeComponent {
089:
090: private static final StringManager s_stringMgr = StringManagerFactory
091: .getStringManager(DataTypeTime.class);
092:
093: /** Logger for this class. */
094: private static ILogger s_log = LoggerController
095: .createLogger(DataTypeTime.class);
096:
097: /* whether nulls are allowed or not */
098: private boolean _isNullable;
099:
100: /* table of which we are part (needed for creating popup dialog) */
101: private JTable _table;
102:
103: /* The JTextComponent that is being used for editing */
104: private IRestorableTextComponent _textComponent;
105:
106: /* The CellRenderer used for this data type */
107: //??? For now, use the same renderer as everyone else.
108: //??
109: //?? IN FUTURE: change this to use a new instance of renederer
110: //?? for this data type.
111: private DefaultColumnRenderer _renderer = DefaultColumnRenderer
112: .getInstance();
113:
114: /**
115: * Name of this class, which is needed because the class name is needed
116: * by the static method getControlPanel, so we cannot use something
117: * like getClass() to find this name.
118: */
119: private static final String this ClassName = "net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.DataTypeTime";
120:
121: /** Default date format */
122: private static int DEFAULT_LOCALE_FORMAT = DateFormat.SHORT;
123:
124: /*
125: * Properties settable by the user
126: */
127: // flag for whether we have already loaded the properties or not
128: private static boolean propertiesAlreadyLoaded = false;
129:
130: // flag for whether to use the default Java format (true)
131: // or the Locale-dependent format (false)
132: private static boolean useJavaDefaultFormat = true;
133:
134: // which locale-dependent format to use; short, medium, long, or full
135: private static int localeFormat = DEFAULT_LOCALE_FORMAT;
136:
137: // Whether to force user to enter dates in exact format or use heuristics to guess it
138: private static boolean lenient = true;
139:
140: // The DateFormat object to use for all locale-dependent formatting.
141: // This is reset each time the user changes the previous settings.
142: private static ThreadSafeDateFormat dateFormat = new ThreadSafeDateFormat(
143: localeFormat, true);
144: private boolean _renderExceptionHasBeenLogged;
145:
146: /**
147: * Constructor - save the data needed by this data type.
148: */
149: public DataTypeTime(JTable table, ColumnDisplayDefinition colDef) {
150: _table = table;
151: _colDef = colDef;
152: _isNullable = colDef.isNullable();
153:
154: loadProperties();
155: }
156:
157: /** Internal function to get the user-settable properties from the DTProperties,
158: * if they exist, and to ensure that defaults are set if the properties have
159: * not yet been created.
160: * <P>
161: * This method may be called from different places depending on whether
162: * an instance of this class is created before the user brings up the Session
163: * Properties window. In either case, the data is static and is set only
164: * the first time we are called.
165: */
166: private static void loadProperties() {
167:
168: //set the property values
169: // Note: this may have already been done by another instance of
170: // this DataType created to handle a different column.
171: if (propertiesAlreadyLoaded == false) {
172: // get parameters previously set by user, or set default values
173: useJavaDefaultFormat = true; // set to use the Java default
174: String useJavaDefaultFormatString = DTProperties.get(
175: this ClassName, "useJavaDefaultFormat");
176: if (useJavaDefaultFormatString != null
177: && useJavaDefaultFormatString.equals("false"))
178: useJavaDefaultFormat = false;
179:
180: // get which locale-dependent format to use
181: localeFormat = DateFormat.SHORT; // set to use the Java default
182: String localeFormatString = DTProperties.get(this ClassName,
183: "localeFormat");
184: if (localeFormatString != null)
185: localeFormat = Integer.parseInt(localeFormatString);
186:
187: // use lenient input or force user to enter exact format
188: lenient = true; // set to allow less stringent input
189: String lenientString = DTProperties.get(this ClassName,
190: "lenient");
191: if (lenientString != null && lenientString.equals("false"))
192: lenient = false;
193: }
194: }
195:
196: /**
197: * Return the name of the java class used to hold this data type.
198: */
199: public String getClassName() {
200: return "java.sql.Time";
201: }
202:
203: /**
204: * Determine if two objects of this data type contain the same value.
205: * Neither of the objects is null
206: */
207: public boolean areEqual(Object obj1, Object obj2) {
208: return ((Time) obj1).equals(obj2);
209: }
210:
211: /*
212: * First we have the methods for in-cell and Text-table operations
213: */
214:
215: /**
216: * Render a value into text for this DataType.
217: */
218: public String renderObject(Object value) {
219: // use the Java default date-to-string
220: if (useJavaDefaultFormat == true || value == null)
221: return (String) _renderer.renderObject(value);
222:
223: // use a date formatter
224: try {
225: return (String) _renderer.renderObject(dateFormat
226: .format(value));
227: } catch (Exception e) {
228: if (false == _renderExceptionHasBeenLogged) {
229: _renderExceptionHasBeenLogged = true;
230: s_log.error("Could not format \"" + value
231: + "\" as date type", e);
232: }
233: return (String) _renderer.renderObject(value);
234: }
235: }
236:
237: /**
238: * This Data Type can be edited in a table cell.
239: */
240: public boolean isEditableInCell(Object originalValue) {
241: return true;
242: }
243:
244: /**
245: * See if a value in a column has been limited in some way and
246: * needs to be re-read before being used for editing.
247: * For read-only tables this may actually return true since we want
248: * to be able to view the entire contents of the cell even if it was not
249: * completely loaded during the initial table setup.
250: */
251: public boolean needToReRead(Object originalValue) {
252: // this DataType does not limit the data read during the initial load of the table,
253: // so there is no need to re-read the complete data later
254: return false;
255: }
256:
257: /**
258: * Return a JTextField usable in a CellEditor.
259: */
260: public JTextField getJTextField() {
261: _textComponent = new RestorableJTextField();
262:
263: // special handling of operations while editing this data type
264: ((RestorableJTextField) _textComponent)
265: .addKeyListener(new KeyTextHandler());
266:
267: //
268: // handle mouse events for double-click creation of popup dialog.
269: // This happens only in the JTextField, not the JTextArea, so we can
270: // make this an inner class within this method rather than a separate
271: // inner class as is done with the KeyTextHandler class.
272: //
273: ((RestorableJTextField) _textComponent)
274: .addMouseListener(new MouseAdapter() {
275: public void mousePressed(MouseEvent evt) {
276: if (evt.getClickCount() == 2) {
277: MouseEvent tableEvt = SwingUtilities
278: .convertMouseEvent(
279: (RestorableJTextField) DataTypeTime.this ._textComponent,
280: evt,
281: DataTypeTime.this ._table);
282: CellDataPopup.showDialog(
283: DataTypeTime.this ._table,
284: DataTypeTime.this ._colDef,
285: tableEvt, true);
286: }
287: }
288: }); // end of mouse listener
289:
290: return (JTextField) _textComponent;
291: }
292:
293: /**
294: * Implement the interface for validating and converting to internal object.
295: * Null is a valid successful return, so errors are indicated only by
296: * existance or not of a message in the messageBuffer.
297: */
298: public Object validateAndConvert(String value,
299: Object originalValue, StringBuffer messageBuffer) {
300: // handle null, which is shown as the special string "<null>"
301: if (value.equals("<null>") || value.equals(""))
302: return null;
303:
304: // Do the conversion into the object in a safe manner
305: try {
306: if (useJavaDefaultFormat) {
307: // allow the user to enter just the hour or just hour and minute
308: // and assume the un-entered values are 0
309: int firstColon = value.indexOf(":");
310: if (firstColon == -1) {
311: // user just entered the hour, so append min & sec
312: value = value + ":0:0";
313: } else {
314: // user entered hour an min. See if they also entered secs
315: if (value.indexOf(":", firstColon + 1) == -1) {
316: // user did not enter seconds
317: value = value + ":0";
318: }
319: }
320: Object obj = Time.valueOf(value);
321: return obj;
322: } else {
323: // use the DateFormat to parse
324: java.util.Date javaDate = dateFormat.parse(value);
325: return new Time(javaDate.getTime());
326: }
327: } catch (Exception e) {
328: messageBuffer.append(e.toString() + "\n");
329: //?? do we need the message also, or is it automatically part of the toString()?
330: //messageBuffer.append(e.getMessage());
331: return null;
332: }
333: }
334:
335: /**
336: * If true, this tells the PopupEditableIOPanel to use the
337: * binary editing panel rather than a pure text panel.
338: * The binary editing panel assumes the data is an array of bytes,
339: * converts it into text form, allows the user to change how that
340: * data is displayed (e.g. Hex, Decimal, etc.), and converts
341: * the data back from text to bytes when the user editing is completed.
342: * If this returns false, this DataType class must
343: * convert the internal data into a text string that
344: * can be displayed (and edited, if allowed) in a TextField
345: * or TextArea, and must handle all
346: * user key strokes related to editing of that data.
347: */
348: public boolean useBinaryEditingPanel() {
349: return false;
350: }
351:
352: /*
353: * Now the functions for the Popup-related operations.
354: */
355:
356: /**
357: * Returns true if data type may be edited in the popup,
358: * false if not.
359: */
360: public boolean isEditableInPopup(Object originalValue) {
361: return true;
362: }
363:
364: /*
365: * Return a JTextArea usable in the CellPopupDialog
366: * and fill in the value.
367: */
368: public JTextArea getJTextArea(Object value) {
369: _textComponent = new RestorableJTextArea();
370:
371: // value is a simple string representation of the data,
372: // the same one used in Text and in-cell operations.
373: ((RestorableJTextArea) _textComponent)
374: .setText(renderObject(value));
375:
376: // special handling of operations while editing this data type
377: ((RestorableJTextArea) _textComponent)
378: .addKeyListener(new KeyTextHandler());
379:
380: return (RestorableJTextArea) _textComponent;
381: }
382:
383: /**
384: * Validating and converting in Popup is identical to cell-related operation.
385: */
386: public Object validateAndConvertInPopup(String value,
387: Object originalValue, StringBuffer messageBuffer) {
388: return validateAndConvert(value, originalValue, messageBuffer);
389: }
390:
391: /*
392: * The following is used in both cell and popup operations.
393: */
394:
395: /*
396: * Internal class for handling key events during editing
397: * of both JTextField and JTextArea.
398: */
399: private class KeyTextHandler extends BaseKeyTextHandler {
400: public void keyTyped(KeyEvent e) {
401: char c = e.getKeyChar();
402:
403: // as a coding convenience, create a reference to the text component
404: // that is typecast to JTextComponent. this is not essential, as we
405: // could typecast every reference, but this makes the code cleaner
406: JTextComponent _theComponent = (JTextComponent) DataTypeTime.this ._textComponent;
407: String text = _theComponent.getText();
408:
409: // tabs and newlines get put into the text before this check,
410: // so remove them
411: // This only applies to Popup editing since these chars are
412: // not passed to this level by the in-cell editor.
413: if (c == KeyEvent.VK_TAB || c == KeyEvent.VK_ENTER) {
414: // remove all instances of the offending char
415: int index = text.indexOf(c);
416: if (index == text.length() - 1) {
417: text = text.substring(0, text.length() - 1); // truncate string
418: } else {
419: text = text.substring(0, index)
420: + text.substring(index + 1);
421: }
422: ((IRestorableTextComponent) _theComponent)
423: .updateText(text);
424: _theComponent.getToolkit().beep();
425: e.consume();
426: }
427:
428: // handle cases of null
429: // The processing is different when nulls are allowed and when they are not.
430: //
431:
432: if (DataTypeTime.this ._isNullable) {
433:
434: // user enters something when field is null
435: if (text.equals("<null>")) {
436: if ((c == KeyEvent.VK_BACK_SPACE)
437: || (c == KeyEvent.VK_DELETE)) {
438: // delete when null => original value
439: DataTypeTime.this ._textComponent.restoreText();
440: e.consume();
441: } else {
442: // non-delete when null => clear field and add text
443: DataTypeTime.this ._textComponent.updateText("");
444: // fall through to normal processing of this key stroke
445: }
446: } else {
447: // check for user deletes last thing in field
448: if ((c == KeyEvent.VK_BACK_SPACE)
449: || (c == KeyEvent.VK_DELETE)) {
450: if (text.length() <= 1) {
451: // about to delete last thing in field, so replace with null
452: DataTypeTime.this ._textComponent
453: .updateText("<null>");
454: e.consume();
455: }
456: }
457: }
458: } else {
459: // field is not nullable
460: //
461: handleNotNullableField(text, c, e, _textComponent);
462: }
463: }
464: }
465:
466: /*
467: * DataBase-related functions
468: */
469:
470: /**
471: * On input from the DB, read the data from the ResultSet into the appropriate
472: * type of object to be stored in the table cell.
473: */
474: public Object readResultSet(ResultSet rs, int index,
475: boolean limitDataRead) throws java.sql.SQLException {
476:
477: Time data = rs.getTime(index);
478: if (rs.wasNull())
479: return null;
480: else
481: return data;
482: }
483:
484: /**
485: * When updating the database, generate a string form of this object value
486: * that can be used in the WHERE clause to match the value in the database.
487: * A return value of null means that this column cannot be used in the WHERE
488: * clause, while a return of "null" (or "is null", etc) means that the column
489: * can be used in the WHERE clause and the value is actually a null value.
490: * This function must also include the column label so that its output
491: * is of the form:
492: * "columnName = value"
493: * or
494: * "columnName is null"
495: * or whatever is appropriate for this column in the database.
496: */
497: public String getWhereClauseValue(Object value,
498: ISQLDatabaseMetaData md) {
499: if (value == null || value.toString() == null
500: || value.toString().length() == 0)
501: return _colDef.getLabel() + " IS NULL";
502: else
503: return _colDef.getLabel() + "={t '" + value.toString()
504: + "'}";
505: }
506:
507: /**
508: * When updating the database, insert the appropriate datatype into the
509: * prepared statment at the given variable position.
510: */
511: public void setPreparedStatementValue(PreparedStatement pstmt,
512: Object value, int position) throws java.sql.SQLException {
513: if (value == null) {
514: pstmt.setNull(position, _colDef.getSqlType());
515: } else {
516: pstmt.setTime(position, ((Time) value));
517: }
518: }
519:
520: /**
521: * Get a default value for the table used to input data for a new row
522: * to be inserted into the DB.
523: */
524: public Object getDefaultValue(String dbDefaultValue) {
525: if (dbDefaultValue != null) {
526: // try to use the DB default value
527: StringBuffer mbuf = new StringBuffer();
528: Object newObject = validateAndConvert(dbDefaultValue, null,
529: mbuf);
530:
531: // if there was a problem with converting, then just fall through
532: // and continue as if there was no default given in the DB.
533: // Otherwise, use the converted object
534: if (mbuf.length() == 0)
535: return newObject;
536: }
537:
538: // no default in DB. If nullable, use null.
539: if (_isNullable)
540: return null;
541:
542: // field is not nullable, so create a reasonable default value
543: return new Time(new java.util.Date().getTime());
544: }
545:
546: /*
547: * File IO related functions
548: */
549:
550: /**
551: * Say whether or not object can be exported to and imported from
552: * a file. We put both export and import together in one test
553: * on the assumption that all conversions can be done both ways.
554: */
555: public boolean canDoFileIO() {
556: return true;
557: }
558:
559: /**
560: * Read a file and construct a valid object from its contents.
561: * Errors are returned by throwing an IOException containing the
562: * cause of the problem as its message.
563: * <P>
564: * DataType is responsible for validating that the imported
565: * data can be converted to an object, and then must return
566: * a text string that can be used in the Popup window text area.
567: * This object-to-text conversion is the same as is done by
568: * the DataType object internally in the getJTextArea() method.
569: *
570: * <P>
571: * File is assumed to be and ASCII string of digits
572: * representing a value of this data type.
573: */
574: public String importObject(FileInputStream inStream)
575: throws IOException {
576:
577: InputStreamReader inReader = new InputStreamReader(inStream);
578:
579: int fileSize = inStream.available();
580:
581: char charBuf[] = new char[fileSize];
582:
583: int count = inReader.read(charBuf, 0, fileSize);
584:
585: if (count != fileSize)
586: throw new IOException("Could read only " + count
587: + " chars from a total file size of " + fileSize
588: + ". Import failed.");
589:
590: // convert file text into a string
591: // Special case: some systems tack a newline at the end of
592: // the text read. Assume that if last char is a newline that
593: // we want everything else in the line.
594: String fileText;
595: if (charBuf[count - 1] == KeyEvent.VK_ENTER)
596: fileText = new String(charBuf, 0, count - 1);
597: else
598: fileText = new String(charBuf);
599:
600: // test that the string is valid by converting it into an
601: // object of this data type
602: StringBuffer messageBuffer = new StringBuffer();
603: validateAndConvertInPopup(fileText, null, messageBuffer);
604: if (messageBuffer.length() > 0) {
605: // convert number conversion issue into IO issue for consistancy
606: throw new IOException(
607: "Text does not represent data of type "
608: + getClassName() + ". Text was:\n"
609: + fileText);
610: }
611:
612: // return the text from the file since it does
613: // represent a valid data value
614: return fileText;
615: }
616:
617: /**
618: * Construct an appropriate external representation of the object
619: * and write it to a file.
620: * Errors are returned by throwing an IOException containing the
621: * cause of the problem as its message.
622: * <P>
623: * DataType is responsible for validating that the given text
624: * text from a Popup JTextArea can be converted to an object.
625: * This text-to-object conversion is the same as validateAndConvertInPopup,
626: * which may be used internally by the object to do the validation.
627: * <P>
628: * The DataType object must flush and close the output stream before returning.
629: * Typically it will create another object (e.g. an OutputWriter), and
630: * that is the object that must be flushed and closed.
631: *
632: * <P>
633: * File is assumed to be and ASCII string of digits
634: * representing a value of this data type.
635: */
636: public void exportObject(FileOutputStream outStream, String text)
637: throws IOException {
638:
639: OutputStreamWriter outWriter = new OutputStreamWriter(outStream);
640:
641: // check that the text is a valid representation
642: StringBuffer messageBuffer = new StringBuffer();
643: validateAndConvertInPopup(text, null, messageBuffer);
644: if (messageBuffer.length() > 0) {
645: // there was an error in the conversion
646: throw new IOException(new String(messageBuffer));
647: }
648:
649: // just send the text to the output file
650: outWriter.write(text);
651: outWriter.flush();
652: outWriter.close();
653: }
654:
655: /*
656: * Property change control panel
657: */
658:
659: /**
660: * Generate a JPanel containing controls that allow the user
661: * to adjust the properties for this DataType.
662: * All properties are static accross all instances of this DataType.
663: * However, the class may choose to apply the information differentially,
664: * such as keeping a list (also entered by the user) of table/column names
665: * for which certain properties should be used.
666: * <P>
667: * This is called ONLY if there is at least one property entered into the DTProperties
668: * for this class.
669: * <P>
670: * Since this method is called by reflection on the Method object derived from this class,
671: * it does not need to be included in the Interface.
672: * It would be nice to include this in the Interface for consistancy, documentation, etc,
673: * but the Interface does not seem to like static methods.
674: */
675: public static OkJPanel getControlPanel() {
676:
677: /*
678: * If you add this method to one of the standard DataTypes in the
679: * fw/datasetviewer/cellcomponent directory, you must also add the name
680: * of that DataType class to the list in CellComponentFactory, method
681: * getControlPanels, variable named initialClassNameList.
682: * If the class is being registered with the factory using registerDataType,
683: * then you should not include the class name in the list (it will be found
684: * automatically), but if the DataType is part of the case statement in the
685: * factory method getDataTypeObject, then it does need to be explicitly listed
686: * in the getControlPanels method also.
687: */
688:
689: // if this panel is called before any instances of the class have been
690: // created, we need to load the properties from the DTProperties.
691: loadProperties();
692:
693: return new TimeOkJPanel();
694: }
695:
696: // Class that displays the various formats available for dates
697: public static class DateFormatTypeCombo extends JComboBox {
698: private static final long serialVersionUID = 1L;
699:
700: public DateFormatTypeCombo() {
701: // i18n[dataTypeTime.full=Full ({0})]
702: addItem(s_stringMgr.getString("dataTypeTime.full",
703: DateFormat.getTimeInstance(DateFormat.FULL).format(
704: new java.util.Date())));
705: // i18n[dataTypeTime.long=Long ({0})]
706: addItem(s_stringMgr.getString("dataTypeTime.long",
707: DateFormat.getTimeInstance(DateFormat.LONG).format(
708: new java.util.Date())));
709: // i18n[dataTypeTime.medium=Medium ({0})]
710: addItem(s_stringMgr.getString("dataTypeTime.medium",
711: DateFormat.getTimeInstance(DateFormat.MEDIUM)
712: .format(new java.util.Date())));
713: // i18n[dataTypeTime.short=Short ({0})]
714: addItem(s_stringMgr.getString("dataTypeTime.short",
715: DateFormat.getTimeInstance(DateFormat.SHORT)
716: .format(new java.util.Date())));
717: }
718:
719: public void setSelectedIndex(int option) {
720: if (option == DateFormat.SHORT)
721: super .setSelectedIndex(3);
722: else if (option == DateFormat.MEDIUM)
723: super .setSelectedIndex(2);
724: else if (option == DateFormat.LONG)
725: super .setSelectedIndex(1);
726: else
727: super .setSelectedIndex(0);
728: }
729:
730: public int getValue() {
731: if (getSelectedIndex() == 3)
732: return DateFormat.SHORT;
733: else if (getSelectedIndex() == 2)
734: return DateFormat.MEDIUM;
735: else if (getSelectedIndex() == 1)
736: return DateFormat.LONG;
737: else
738: return DateFormat.FULL;
739: }
740: }
741:
742: /**
743: * Inner class that extends OkJPanel so that we can call the ok()
744: * method to save the data when the user is happy with it.
745: */
746: private static class TimeOkJPanel extends OkJPanel {
747: private static final long serialVersionUID = 1L;
748: /*
749: * GUI components - need to be here because they need to be
750: * accessible from the event handlers to alter each other's state.
751: */
752: // check box for whether to use Java Default or a Locale-dependent format
753:
754: private JCheckBox useJavaDefaultFormatChk =
755: // i18n[dataTypeTime.useDefaultFormat=Use default format ({0})]
756: new JCheckBox(s_stringMgr.getString(
757: "dataTypeTime.useDefaultFormat", new Time(
758: new java.util.Date().getTime()).toString()));
759:
760: // label for the date format combo, used to enable/disable text
761: // i18n[dataTypeTime.useDefaultFormat2= or locale-dependent format:]
762: private RightLabel dateFormatTypeDropLabel = new RightLabel(
763: s_stringMgr.getString("dataTypeTime.useDefaultFormat2"));
764:
765: // Combo box for read-all/read-part of blob
766: private DateFormatTypeCombo dateFormatTypeDrop = new DateFormatTypeCombo();
767:
768: // checkbox for whether to interpret input leniently or not
769: // i18n[dataTypeTime.inexact=allow inexact format on input]
770: private JCheckBox lenientChk = new JCheckBox(s_stringMgr
771: .getString("dataTypeTime.inexact"));
772:
773: public TimeOkJPanel() {
774:
775: /* set up the controls */
776: // checkbox for Java default/non-default format
777: useJavaDefaultFormatChk.setSelected(useJavaDefaultFormat);
778: useJavaDefaultFormatChk
779: .addChangeListener(new ChangeListener() {
780: public void stateChanged(ChangeEvent e) {
781: dateFormatTypeDrop
782: .setEnabled(!useJavaDefaultFormatChk
783: .isSelected());
784: dateFormatTypeDropLabel
785: .setEnabled(!useJavaDefaultFormatChk
786: .isSelected());
787: lenientChk
788: .setEnabled(!useJavaDefaultFormatChk
789: .isSelected());
790: }
791: });
792:
793: // Combo box for read-all/read-part of blob
794: dateFormatTypeDrop = new DateFormatTypeCombo();
795: dateFormatTypeDrop.setSelectedIndex(localeFormat);
796:
797: // lenient checkbox
798: lenientChk.setSelected(lenient);
799:
800: // handle cross-connection between fields
801: dateFormatTypeDrop.setEnabled(!useJavaDefaultFormatChk
802: .isSelected());
803: dateFormatTypeDropLabel.setEnabled(!useJavaDefaultFormatChk
804: .isSelected());
805: lenientChk
806: .setEnabled(!useJavaDefaultFormatChk.isSelected());
807:
808: /*
809: * Create the panel and add the GUI items to it
810: */
811:
812: setLayout(new GridBagLayout());
813:
814: // i18n[dataTypeTime.typeTime=Time (SQL type 92)]
815: setBorder(BorderFactory.createTitledBorder(s_stringMgr
816: .getString("dataTypeTime.typeTime")));
817: final GridBagConstraints gbc = new GridBagConstraints();
818: gbc.fill = GridBagConstraints.HORIZONTAL;
819: gbc.insets = new Insets(4, 4, 4, 4);
820: gbc.anchor = GridBagConstraints.WEST;
821:
822: gbc.gridx = 0;
823: gbc.gridy = 0;
824:
825: gbc.gridwidth = GridBagConstraints.REMAINDER;
826: add(useJavaDefaultFormatChk, gbc);
827:
828: gbc.gridwidth = 1;
829: gbc.gridx = 0;
830: ++gbc.gridy;
831: add(dateFormatTypeDropLabel, gbc);
832:
833: ++gbc.gridx;
834: add(dateFormatTypeDrop, gbc);
835:
836: gbc.gridx = 0;
837: ++gbc.gridy;
838: add(lenientChk, gbc);
839:
840: } // end of constructor for inner class
841:
842: /**
843: * User has clicked OK in the surrounding JPanel,
844: * so save the current state of all variables
845: */
846: public void ok() {
847: // get the values from the controls and set them in the static properties
848: useJavaDefaultFormat = useJavaDefaultFormatChk.isSelected();
849: DTProperties.put(this ClassName, "useJavaDefaultFormat",
850: Boolean.valueOf(useJavaDefaultFormat).toString());
851:
852: localeFormat = dateFormatTypeDrop.getValue();
853: dateFormat = new ThreadSafeDateFormat(localeFormat, true); // lenient is set next
854: DTProperties.put(this ClassName, "localeFormat", Integer
855: .toString(localeFormat));
856:
857: lenient = lenientChk.isSelected();
858: dateFormat.setLenient(lenient);
859: DTProperties.put(this ClassName, "lenient", Boolean.valueOf(
860: lenient).toString());
861: }
862:
863: } // end of inner class
864:
865: }
|