/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ValueEditorTableCellEditor.java
* Created: prior to 2002
* By: ?
*/
package org.openquark.gems.client.valueentry;
import java.awt.Component;
import java.util.EventObject;
import java.util.List;
import java.util.Vector;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.table.TableCellEditor;
import org.openquark.cal.valuenode.ValueNode;
/**
* The default TableCellEditor for ValueEditors that use tables.
* It will be a ValueEntryPanel of the correct type for the cell.
*/
public class ValueEditorTableCellEditor extends ValueEntryPanel implements TableCellEditor {
private static final long serialVersionUID = 3959043138610802855L;
/** The list of listeners. */
private final List<CellEditorListener> listenerList;
// If the cell editor is being currently used, the value is the column currently being edited.
// If the cell editor is not currently used, the value is the last column edited.
// If the cell editor was never used, then the value defaults to 0 (done in constructor).
private int editColumn;
/**
* Constructor for a ValueEditorTableCellEditor.
* @param parentTableValueEditor the parent table value editor
* @param valueEditorHierarchyManager
*/
public ValueEditorTableCellEditor(TableValueEditor parentTableValueEditor, ValueEditorHierarchyManager valueEditorHierarchyManager) {
super(valueEditorHierarchyManager);
setParentValueEditor(parentTableValueEditor);
// use a synchronized list.
listenerList = new Vector<CellEditorListener>();
editColumn = 0;
}
/**
* Return the parent table value editor.
* @return the parent table value editor
*/
private TableValueEditor getParentTableValueEditor() {
return (TableValueEditor) getParentValueEditor();
}
/**
* If the cell editor is being currently used, the value is the column currently being edited.
* If the cell editor is not currently used, the value is the last column edited.
* If the cell editor was never used, then the value defaults to 0 (done in constructor).
* Creation date: (01/03/01 9:12:55 AM)
* @return int
*/
public int getEditColumn() {
return editColumn;
}
/**
* {@inheritDoc}
*/
@Override
public void editorActivated() {
super.editorActivated();
getParentTableValueEditor().handleCellActivated();
updateLaunchEditorButton();
}
/**
* Enable or disable the launch editor button according to the current state of things.
*/
private void updateLaunchEditorButton() {
if (isEditable()) {
// Always enable editor button
getLaunchEditorButton().setEnabled(true);
} else {
// The value node may not be set if the editor is being initialized..
// TODO: remove this line (initialize with a value node)
if (getValueNode() == null) {
return;
}
getLaunchEditorButton().setEnabled(true);
}
}
/*
* Methods implementing TableCellEditor ************************************************************
*/
/**
* {@inheritDoc}
*/
public Object getCellEditorValue() {
return getValueNode();
}
/**
* {@inheritDoc}
*/
public boolean isCellEditable(EventObject anEvent) {
return true;
}
/**
* {@inheritDoc}
*/
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
/**
* {@inheritDoc}
* This is a commit.
*/
public boolean stopCellEditing() {
commitValue();
// Note that the JTable parent automatically registers itself as a listener on this cell editor.
// When it receives editingStopped() events, it calls on the model to setValueAt() the cell that is currently being edited.
// This conflicts with the value editor's current mechanism of commiting values, which uses close().
//
// For now, this section is commented out to avoid this. This means that setValueAt() is never called (for now).
// However, this introduces a problem in the API that any other listeners will never be notified.
// In the future it might be nicer if we could allow the JTable to setValueAt() in the normal way, have it fire tableCellUpdated()
// events, have those in turn fire valueCommitted() events, which then in turn results in a call to commitChildChanges().
// The nice feature of this change would be that it should allow us to remove the hack at the beginning of
// getTableCellEditorComponent().
//
// ChangeEvent ce = new ChangeEvent(this);
// for (int i = 0, listenerCount = listenerList.size(); i < listenerCount; i++) {
// ((CellEditorListener) listenerList.get(i)).editingStopped(ce);
// }
return true;
}
/**
* {@inheritDoc}
*/
public void cancelCellEditing() {
// This is a hack!
// JTable for JDK 1.4 has a "CellEditorRemover" that is supposed to cancel cell editing when focus
// is shifted out of the table. Since we put all child editors on the TableTop rather than the table
// we have a problem with this. For JDK 1.4.0 there is a bug in the CellEditorRemover so that it only
// actually cancels the first time it trys to and never again. This will probably be fixed for the next
// release. My hack is to check here and return without doing anything if the component with focus
// belongs to a child editor. This should normally only cause problems with things if the table's
// "Remove" button is pressed because it uses this function. However, when a child editor is opened (and
// has focus) the Remove button is disabled. As a result, the Remove button can never be pressed
// unless there is no child editor so my little hack does not come into play.
// Bug id: 4503845, 4709394 - fixable in jdk1.4.1.
//
// Do not cancel editing if there is a child editor open and it (or one of its components) has focus.
ValueEditor childEditor = valueEditorHierarchyManager.getChildEditor(this);
if (childEditor != null && childEditor.hasOverallFocus()) {
return;
}
// Cancel editing...
cancelValue();
// See note for stopCellEditing()
// ChangeEvent ce = new ChangeEvent(this);
// for (int i = 0, listenerCount = listenerList.size(); i < listenerCount; i++) {
// ((CellEditorListener) listenerList.get(i)).editingCanceled(ce);
// }
}
/**
* {@inheritDoc}
*/
public void addCellEditorListener(CellEditorListener l) {
listenerList.add(l);
}
/**
* {@inheritDoc}
*/
public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(l);
}
/**
* {@inheritDoc}
* Value must be of type ValueNode.
* The use of a ValueEditorTableModel should ensure this.
*/
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
// HACK: a component may attempt to set this cell editor to edit a new cell before the old value was committed.
// (eg. click from the child editor for one value in a list onto another value in the list)
if (valueEditorHierarchyManager.getChildEditor(this) != null) {
commitValue();
}
editColumn = column;
setEditorIsClosing(false);
setOwnerValueNode((ValueNode)value);
setInitialValue();
// Add this editor to the hierarchy if it's not in the hierarchy already.
// (If we are clicking on this editor from a child, we do not want to add this editor).
// On close (eg. on stopCellEditing()), it will be removed from the hierarchy, and this will be called again.
// TODO: stuff like this should be handled by the manager itself.
if (!valueEditorHierarchyManager.existsInHierarchy(this)) {
valueEditorHierarchyManager.addEditorToHierarchy(this, getParentValueEditor());
}
// Prevent the user from clicking on the rightmost portion of a ValueEditorTableCellRenderer,
// and suddenly launching the editor.
getLaunchEditorButton().setEnabled(false);
SwingUtilities.invokeLater(new Thread() {
@Override
public void run() {
updateLaunchEditorButton();
valueEditorHierarchyManager.activateCurrentEditor();
}
});
return this;
}
}