/* 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.Units; import org.das2.datum.format.TimeDatumFormatter; import org.das2.datum.Datum; import org.das2.datum.TimeLocationUnits; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; 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; /** * Editor for a Datum. * @author Edward West * @see org.das2.datum.Datum */ public class DatumEditor implements PropertyEditor, TableCellEditor { private JTextField editor; private JPanel panel; private JButton unitsButton; private Units units = Units.dimensionless; private ActionListener actionListener; private Datum value; private EventListenerList listeners; private PropertyChangeSupport pcs = new PropertyChangeSupport(this); public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { pcs.removePropertyChangeListener(propertyName, listener); } @Override public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { pcs.addPropertyChangeListener(propertyName, listener); } @Override public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } /** Creates a new instance of DatumEditor */ public DatumEditor() { } private void initGui() { initComponents(); installListeners(); initToolTips(); unitsButton.setVisible(false); panel.setFocusable(true); } private void maybeInitGui() { if (panel == null) { initGui(); } } private void initComponents() { panel = new JPanel(); panel.setLayout(new BorderLayout()); editor = new JTextField(8); if (value != null) setDatum(value); editor.setFocusable(false); panel.add(editor, BorderLayout.CENTER); unitsButton = new JButton(); unitsButton.setFocusable(false); unitsButton.setToolTipText("units selection"); if ( units!=null ) { setUnits(units); // set labels and such } panel.add(unitsButton, BorderLayout.EAST); } private void installListeners() { UniversalListener ul = new UniversalListener(); editor.addMouseListener(ul); unitsButton.addMouseListener(ul); panel.addKeyListener(ul); panel.addFocusListener(ul); } private void initToolTips() { ToolTipManager.sharedInstance().registerComponent(panel); } public void setColumns(int columns) { editor.setColumns(columns); } /** * @param value the Datum object to be editted. * @throws IllegalArgumentException if value is not a Datum */ @Override public void setValue(Object value) { if (value instanceof Datum) { setDatum((Datum) value); } else { throw new IllegalArgumentException(); } } /** * @param datum the Datum object to be editted. */ private void setDatum(Datum datum) { Datum oldValue = value; value = datum; Units u = datum.getUnits(); if (editor != null) { if (datum.getUnits() instanceof TimeLocationUnits) { editor.setText(TimeDatumFormatter.DEFAULT.format(datum, u)); } else { editor.setText(datum.getFormatter().format(datum, u)); } } setUnits(u); if (oldValue != value && oldValue != null && !oldValue.equals(value)) { pcs.firePropertyChange("value", oldValue, value); } } @Override public void setAsText(String text) throws IllegalArgumentException { try { setDatum(units.parse(text)); } catch (ParseException pe) { IllegalArgumentException iae = new IllegalArgumentException(pe); throw iae; } } @Override public Object getValue() { return getDatum(); } public Datum getDatum() { try { Datum d; if (editor != null) { String text = editor.getText(); d = units.parse(text); if (!d.equals(value)) { Datum oldValue = value; value = d; pcs.firePropertyChange("value", oldValue, value); } } return value; } catch (ParseException e) { if (value != null) { setDatum(value); // cause reformat of old Datum return value; } else { return null; } } } @Override public String getAsText() { Datum v = getDatum(); if (v == null) { return null; } else { return v.toString(); } } public void setUnits(Units units) { if (unitsButton != null) { // 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 void fireActionPerformed() { setDatum(getDatum()); 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. */ @Override 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. */ @Override 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 */ @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { fireActionPerformed(); } else { forwardKeyEvent(e); } } @Override public void keyReleased(KeyEvent e) { forwardKeyEvent(e); } @Override 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 */ @Override public void mousePressed(MouseEvent e) { panel.requestFocusInWindow(); } /** Unused */ @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } @Override public void mouseClicked(MouseEvent e) { } @Override 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 */ @Override public boolean supportsCustomEditor() { return true; } /** @see java.beans.PropertyEditor#getCustomEditor() * @return this */ @Override public Component getCustomEditor() { maybeInitGui(); return panel; } /** * This PropertyEditor implementation does not support generating java * code. * @return The string "???" */ @Override public String getJavaInitializationString() { return "???"; } /** * This PropertyEditor implementation does not support enumerated * values. * @return null */ @Override public String[] getTags() { return null; } @Override public boolean isPaintable() { return false; } @Override public void paintValue(Graphics g, Rectangle r) { } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setValue(value); maybeInitGui(); return panel; } @Override public void addCellEditorListener(CellEditorListener l) { if (listeners == null) { listeners = new EventListenerList(); } listeners.add(CellEditorListener.class, l); } @Override public void removeCellEditorListener(CellEditorListener l) { if (listeners != null) { listeners.remove(CellEditorListener.class, l); } } /** @see javax.swing.CellEditor#isCellEditable(java.util.EventObject) * @return true */ @Override public boolean isCellEditable(EventObject anEvent) { return true; } /** @see javax.swing.CellEditor#shouldSelectCell(java.util.EventObject) * @return true */ @Override public boolean shouldSelectCell(EventObject anEvent) { return true; } /** Returns the value stored in this editor. * @return the current value being edited */ @Override public Object getCellEditorValue() { return getDatum(); } @Override public boolean stopCellEditing() { if (getDatum() == null) { return false; } fireEditingStopped(); return true; } @Override public void cancelCellEditing() { fireEditingCanceled(); } private ChangeEvent evt; private void fireEditingStopped() { if ( listeners==null ) return; 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() { if ( listeners==null ) return; 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); } } } }