/* File: DatumEditor.java * Copyright (C) 2002-2003 The University of Iowa * * Created on October 1, 2003, 9:20 AM * by Edward West * * This file is part of the das2 library. * * das2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.das2.components; import org.das2.datum.DatumRange; import org.das2.datum.Units; import org.das2.datum.DatumRangeUtil; import org.das2.datum.TimeLocationUnits; import java.awt.*; import java.awt.event.*; import java.beans.PropertyEditor; import java.text.ParseException; import java.util.EventObject; import javax.swing.*; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.EventListenerList; import javax.swing.table.TableCellEditor; import org.das2.DasApplication; import org.das2.datum.UnitsUtil; /** * * @author Edward West */ public class DatumRangeEditor extends JComponent implements PropertyEditor, TableCellEditor { private JTextField editor; private JButton unitsButton; private Units units = Units.dimensionless; private ActionListener actionListener; private DatumRange value; private EventListenerList listeners; /** Creates a new instance of DatumEditor */ public DatumRangeEditor() { initComponents(); installListeners(); initToolTips(); unitsButton.setVisible(false); setFocusable(true); } private void initComponents() { setLayout(new BorderLayout()); editor = new JTextField(8); editor.setFocusable(false); add(editor, BorderLayout.CENTER); unitsButton = new JButton(); unitsButton.setFocusable(false); unitsButton.setToolTipText("units selection"); add(unitsButton, BorderLayout.EAST); } private void installListeners() { UniversalListener ul = new UniversalListener(); editor.addMouseListener(ul); unitsButton.addMouseListener(ul); addKeyListener(ul); addFocusListener(ul); } private void initToolTips() { ToolTipManager.sharedInstance().registerComponent(this); } public void setColumns(int columns) { editor.setColumns(columns); } /** * @param value the DatumRange to be editted. * @throws IllegalArgumentException if value is not a Datum */ public void setValue(Object value) { if (value instanceof DatumRange) { setDatumRange((DatumRange)value); } else { throw new IllegalArgumentException(); } } /** * @param datumRange the DatumRange to be edited. */ private void setDatumRange(DatumRange datumRange ) { DatumRange oldValue = value; // if ( datumRange.min().gt( datumRange.max() ) ) { // System.err.println("how did we get here!"); // } value = datumRange; Units u= datumRange.getUnits(); String text= datumRange.toString(); if ( !UnitsUtil.isTimeLocation(units) ) { if ( text.endsWith( u.toString() ) && u.toString().length()>7 ) { int n= text.length(); text= text.substring(0,n-u.toString().length()); } } // System.err.println("thetext="+text); // try { // DatumRangeUtil.parseTimeRange(text); // } catch ( ParseException ex ) { // ex.printStackTrace();; // } catch ( IllegalArgumentException ex ) { // ex.printStackTrace();; // datumRange.toString(); // } editor.setText( text ); setUnits(u); if (oldValue != value && oldValue != null && !oldValue.equals(value)) { firePropertyChange("value", oldValue, value); } } public void setAsText(String text) throws IllegalArgumentException { try { setDatumRange( parseText( text ) ); } catch (ParseException pe) { IllegalArgumentException iae = new IllegalArgumentException(pe); throw iae; } } public Object getValue() { return getDatumRange(); } private DatumRange parseText( String text ) throws ParseException { DatumRange result=null; if ( units instanceof TimeLocationUnits ) { result= DatumRangeUtil.parseTimeRange( text ); } else { result= DatumRangeUtil.parseDatumRange( text, value ); } return result; } /** * prevent displaying same message so many times... */ private String lastErrorText= null; private long lastErrorTime= 0; private void showErrorUsage( String text, String why ) { if ( !DasApplication.getDefaultApplication().isHeadless() ) { if ( text!=null && text.equals(lastErrorText) && (System.currentTimeMillis()-lastErrorTime)<5000 ) { return; } if ( why!=null ) { JOptionPane.showMessageDialog( this, "Unable to accept \""+text+"\"
"+why+"" ); } else { JOptionPane.showMessageDialog( this, "Unable to accept \""+text+"\"" ); } lastErrorText= text; lastErrorTime= System.currentTimeMillis(); } } /** * attempt to read the datum range in the TextField. If it is not * parseable, then display an error message and return to the old value. * @return */ public DatumRange getDatumRange() { String text = editor.getText(); try { DatumRange dr = parseText(text); if ( !Double.isFinite(dr.min().doubleValue(dr.getUnits())) ) { if ( !Double.isFinite(value.min().doubleValue(value.getUnits())) ) { editor.setText("1 to 100"); dr = parseText(text); } else { editor.setText(value.toString()); return value; } } if ( !allowZeroWidth && dr.width().value()==0 ) { showErrorUsage( text, "zero-width ranges are not allowed." ); return value; } if (!dr.equals(value) ) { DatumRange oldValue = value; value = dr; firePropertyChange("value", oldValue, value); } return value; } catch (ParseException e) { if ( value!=null ) { setDatumRange( value ); // cause reformat of old Datum if ( UnitsUtil.isTimeLocation(value.getUnits()) ) { showErrorUsage( text, "String cannot be parsed to time range" ); } else { showErrorUsage( text, "String cannot be parsed to range with units \""+value.getUnits()+"\"" ); } return value; } else { return null; } } catch ( IllegalArgumentException e ) { if ( value!=null ) { setDatumRange( value ); // cause reformat of old Datum if ( e.getMessage().contains("min > max") ) { showErrorUsage( text, "Min cannot be greater than max" ); } else { showErrorUsage( text, e.getMessage() ); } return value; } else { return null; } } } public String getAsText() { //String text= editor.getText(); Object value = getDatumRange(); if (value == null) { return null; } return editor.getText(); } public void setUnits(Units units) { // if (units instanceof TimeLocationUnits) { unitsButton.setVisible(false); // } else { // unitsButton.setVisible(true); // String unitsStr= units.toString(); // if ( unitsStr.length()>10 ) unitsStr= unitsStr.substring(0,9)+"..."; // unitsButton.setText(unitsStr); // unitsButton.setToolTipText(units.toString()); // don't abbreviate // } this.units = units; } public Units getUnits() { return units; } private boolean allowZeroWidth = true; public static final String PROP_ALLOWZEROWIDTH = "allowZeroWidth"; public boolean isAllowZeroWidth() { return allowZeroWidth; } public void setAllowZeroWidth(boolean allowZeroWidth) { boolean oldAllowZeroWidth = this.allowZeroWidth; this.allowZeroWidth = allowZeroWidth; firePropertyChange(PROP_ALLOWZEROWIDTH, oldAllowZeroWidth, allowZeroWidth); } private void fireActionPerformed() { setDatumRange(getDatumRange()); if (actionListener != null) { actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "DatumEditor")); } } public void addActionListener(ActionListener al) { actionListener = AWTEventMulticaster.add(actionListener, al); } public void removeActionListener(ActionListener al) { actionListener = AWTEventMulticaster.remove(actionListener, al); } /* Why all one class? To reduce the number of object created. * The purposes of the listeners are not necessarily related and it is * generally not a good idea to implement multiple interfaces unless there * is some sort of correlation between them. It is okay to do that here * since this class is not a part of the public interface. */ private class UniversalListener implements MouseListener, KeyListener, FocusListener { /** Listens for focus events on the DatumEditor and sets the editor * caret visible when focus is gained. */ public void focusGained(FocusEvent e) { editor.getCaret().setVisible(true); editor.getCaret().setSelectionVisible(true); } /** Listens for focus events on the DatumEditor and sets the editor * caret invisible when focus is lost. */ public void focusLost(FocusEvent e) { editor.getCaret().setVisible(false); editor.getCaret().setSelectionVisible(false); } /** All key events are forwarded to the editor. Except for keyPresses * for VK_ENTER */ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { fireActionPerformed(); } else { forwardKeyEvent(e); } } public void keyReleased(KeyEvent e) { forwardKeyEvent(e); } public void keyTyped(KeyEvent e) { forwardKeyEvent(e); } private void forwardKeyEvent(KeyEvent e) { e.setSource(editor); editor.dispatchEvent(e); } /** Request focus when sub-components are clicked */ public void mousePressed(MouseEvent e) { requestFocusInWindow(); } /** Unused */ public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} } public String getToolTipText(MouseEvent event) { if (unitsButton.getBounds().contains(event.getX(), event.getY())) { return unitsButton.getToolTipText(); } else { return null; } } /** @see java.beans.PropertyEditor#supportsCustomEditor() * @return true */ public boolean supportsCustomEditor() { return true; } /** @see java.beans.PropertyEditor#getCustomEditor() * @return this */ public Component getCustomEditor() { return this; } /** * This PropertyEditor implementation does not support generating java * code. * @return The string "???" */ public String getJavaInitializationString() { return "???"; } /** * This PropertyEditor implementation does not support enumerated * values. * @return null */ public String[] getTags() { return null; } /** @see java.beans.PropertyEditor#isPaintable() * @return false */ public boolean isPaintable() { return false; } /** Does nothing. * @param g * @param r */ public void paintValue(Graphics g, Rectangle r) {} /* CellEditor stuff */ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setValue(value); return this; } /** */ public void addCellEditorListener(CellEditorListener l) { if (listeners == null) { listeners = new EventListenerList(); } listeners.add(CellEditorListener.class, l); } /** */ public void removeCellEditorListener(CellEditorListener l) { if (listeners != null) { listeners.remove(CellEditorListener.class, l); } } /** @see javax.swing.CellEditor#isCellEditable(java.util.EventObject) * @return true */ public boolean isCellEditable(EventObject anEvent) { return true; } /** @see javax.swing.CellEditor#shouldSelectCell(java.util.EventObject) * @return true */ public boolean shouldSelectCell(EventObject anEvent) { return true; } /** Returns the value stored in this editor. * @return the current value being edited */ public Object getCellEditorValue() { return getDatumRange(); } public boolean stopCellEditing() { if (getDatumRange() == null) { return false; } fireEditingStopped(); return true; } public void cancelCellEditing() { fireEditingCanceled(); } private ChangeEvent evt; private void fireEditingStopped() { Object[] l = listeners.getListenerList(); for (int i = 0; i < l.length; i+=2) { if (l[i] == CellEditorListener.class) { CellEditorListener cel = (CellEditorListener)l[i+1]; if (evt == null) { evt = new ChangeEvent(this); } cel.editingStopped(evt); } } } private void fireEditingCanceled() { Object[] l = listeners.getListenerList(); for (int i = 0; i < l.length; i+=2) { if (l[i] == CellEditorListener.class) { CellEditorListener cel = (CellEditorListener)l[i+1]; if (evt == null) { evt = new ChangeEvent(this); } cel.editingCanceled(evt); } } } }