/* * 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. */ /* * TupleValueEditor.java * Created: Feb 13, 2002 * By: Michael Cheng */ package org.openquark.gems.client.valueentry; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumnModel; import org.openquark.cal.compiler.FieldName; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.valuenode.NTupleValueNode; import org.openquark.cal.valuenode.ValueNode; import org.openquark.cal.valuenode.ValueNodeBuilderHelper; /** * A TupleValueEditor displays a list (in order) of the ValueEntryPanels representing the elements of the tuple. * @author Michael Cheng */ public class TupleValueEditor extends TableValueEditor { private static final long serialVersionUID = 8431190568858778573L; /** * This interface defines what drag operations are supported by the * <code>TupleValueEditor</code>. Implementors of this interface will be * called when a certain drag operation is initiated by the user. */ public interface TupleValueDragPointHandler extends ValueEditorDragPointHandler { /** * This method defines the behaviour when the user attempts to drag a portion * of a tuple from the <code>TupleValueEditor</code>. By default, this * method is empty and subclasses are encouraged to override this class to * specify their own drag handling code. * @param dge * @param parentEditor * @param tupleElementIndex * @return boolean */ boolean dragTupleItem(DragGestureEvent dge, TupleValueEditor parentEditor, int tupleElementIndex); } /** * A custom value editor provider for the TupleValueEditor. */ public static class TupleValueEditorProvider extends ValueEditorProvider<TupleValueEditor> { public TupleValueEditorProvider(ValueEditorManager valueEditorManager) { super(valueEditorManager); } /** * {@inheritDoc} */ @Override public boolean canHandleValue(ValueNode valueNode, SupportInfo providerSupportInfo) { return valueNode instanceof NTupleValueNode && hasSupportedTupleTypes((NTupleValueNode)valueNode, providerSupportInfo); } /** * {@inheritDoc} */ @Override public TupleValueEditor getEditorInstance(ValueEditorHierarchyManager valueEditorHierarchyManager, ValueNode valueNode) { TupleValueEditor editor = new TupleValueEditor(valueEditorHierarchyManager, null); editor.setOwnerValueNode(valueNode); return editor; } /** * {@inheritDoc} */ @Override public TupleValueEditor getEditorInstance(ValueEditorHierarchyManager valueEditorHierarchyManager, ValueNodeBuilderHelper valueNodeBuilderHelper, ValueEditorDragManager dragManager, ValueNode valueNode) { TupleValueEditor editor = new TupleValueEditor(valueEditorHierarchyManager, getListTupleDragPointHandler(dragManager)); editor.setOwnerValueNode(valueNode); return editor; } /** * {@inheritDoc} */ @Override public boolean usableForOutput() { return true; } /** * Checks if the value nodes for the tuple elements are supported by value editors. * @param valueNode the tuple value node to check for * @return true if all element value nodes are supported */ private boolean hasSupportedTupleTypes(NTupleValueNode valueNode, SupportInfo providerSupportInfo) { // Notify the info object that the value node's type is supported.. providerSupportInfo.markSupported(valueNode.getTypeExpr()); ValueEditorManager valueEditorManager = getValueEditorManager(); for (int i = 0, size = valueNode.getTupleSize(); i < size; i++) { ValueNode elementValueNode = valueNode.getValueAt(i); if (!valueEditorManager.isSupportedValueNode(elementValueNode, providerSupportInfo)) { return false; } } return true; } /** * A convenient method for casting the drag point handler to the type that is * suitable for the <code>TupleValueEditor</code> to use. If such conversion is not * possible, then this method should return <code>null</code>. * @param dragManager * @return TupleDragPointHandler */ private TupleValueDragPointHandler getListTupleDragPointHandler(ValueEditorDragManager dragManager) { ValueEditorDragPointHandler handler = getDragPointHandler(dragManager); if (handler instanceof TupleValueDragPointHandler) { return (TupleValueDragPointHandler) handler; } return null; } } /** * Listens to drag events from the tuple table header an delegates to the drag point handler * to do the actual work. */ private class TupleDragGestureListener implements DragGestureListener { /** * @see java.awt.dnd.DragGestureListener#dragGestureRecognized(java.awt.dnd.DragGestureEvent) */ public void dragGestureRecognized(DragGestureEvent dge) { if (dge.getTriggerEvent() instanceof MouseEvent) { JTableHeader header = getTableHeader(); TableColumnModel model = header.getColumnModel(); int xPos = ((MouseEvent)dge.getTriggerEvent()).getX(); if (dragPointHandler != null) { dragPointHandler.dragTupleItem(dge, TupleValueEditor.this, model.getColumnIndexAtX(xPos)); } } } } /** The minimum size of this editor. */ private static final Dimension MIN_SIZE = new Dimension(200, -1); /** The maximum size of this editor. */ private static final Dimension MAX_SIZE = new Dimension(600, -1); // Allow this value editor to initiate drag events private final TupleValueDragPointHandler dragPointHandler; /** * TupleValueEditor constructor comment. * @param valueEditorHierarchyManager * @param dragPointHandler */ protected TupleValueEditor(ValueEditorHierarchyManager valueEditorHierarchyManager, TupleValueDragPointHandler dragPointHandler) { super(valueEditorHierarchyManager); this.dragPointHandler = dragPointHandler; setLayout(new BorderLayout()); add(tableScrollPane, BorderLayout.CENTER); // Attach a listener for drag events if (dragPointHandler != null) { JTableHeader tableHeader = getTableHeader(); DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer( tableHeader, DnDConstants.ACTION_COPY_OR_MOVE, new TupleDragGestureListener()); } } /** * @see org.openquark.gems.client.valueentry.ValueEditor#setInitialValue() */ @Override public void setInitialValue() { initSize(MIN_SIZE, MAX_SIZE); // We only care about maximum width for the initial display. // After that we allow the user to resize the editor to any width. setMaxResizeDimension(new Dimension(2048, getSize().height)); } /** * @see org.openquark.gems.client.valueentry.ValueEditor#refreshDisplay() */ @Override public void refreshDisplay() { // Reselect the cell that was previously being edited. selectCell(getSelectedRow(), getSelectedColumn()); } /** * @see org.openquark.gems.client.valueentry.TableValueEditor#createTableModel(org.openquark.cal.valuenode.ValueNode) */ @Override protected ValueEditorTableModel createTableModel(ValueNode valueNode) { return new TupleTableModel((NTupleValueNode) valueNode, valueEditorHierarchyManager); } /** * Get a map from every value node managed by this editor to its least constrained type. * @return Map from every value node managed by this editor to its least constrained type. */ private Map<ValueNode, TypeExpr> getValueNodeToUnconstrainedTypeMap() { Map<ValueNode, TypeExpr> returnMap = new HashMap<ValueNode, TypeExpr>(); // Get the value nodes for the tuple and the items in the tuple. NTupleValueNode currentTupleValue = (NTupleValueNode)getValueNode(); int tupleSize = currentTupleValue.getTupleSize(); // Populate the map // Tuple TypeExpr unconstrainedTupleType = getContext().getLeastConstrainedTypeExpr(); if (unconstrainedTupleType.rootTypeVar() != null) { // not constrained by the context to be a tuple unconstrainedTupleType = TypeExpr.makeTupleType(tupleSize); } returnMap.put(currentTupleValue, unconstrainedTupleType); // Tuple elements Map<FieldName, TypeExpr> hasFieldsMap = unconstrainedTupleType.rootRecordType().getHasFieldsMap(); int j = 0; for (final TypeExpr unconstrainedTupleElementType : hasFieldsMap.values()) { ValueNode currentTupleItem = currentTupleValue.getValueAt(j); returnMap.put(currentTupleItem, unconstrainedTupleElementType); ++j; } return returnMap; } /** * {@inheritDoc} */ @Override public void commitChildChanges(ValueNode oldChild, ValueNode newChild) { // Get the copy of the current value node, type switched if necessary. NTupleValueNode oldValueNode = (NTupleValueNode)getValueNode(); NTupleValueNode newValueNode; if (!oldChild.getTypeExpr().sameType(newChild.getTypeExpr())) { Map<ValueNode, TypeExpr> valueNodeToUnconstrainedTypeMap = getValueNodeToUnconstrainedTypeMap(); Map<ValueNode, ValueNode> commitValueMap = valueEditorManager.getValueNodeCommitHelper().getCommitValues(oldChild, newChild, valueNodeToUnconstrainedTypeMap); newValueNode = (NTupleValueNode)commitValueMap.get(oldValueNode); } else { newValueNode = (NTupleValueNode)oldValueNode.copyValueNode(); } // Modify the new value node so that the old child is replaced by the new child. // Note that the cell editor may now be editing a different column so we have to search for the row that changed // This can happen if one clicks from an editor for one cell to another cell // (eg. in a list of colours, from a colour value editor for one cell, onto the cell editor for another cell.) List<ValueNode> currentChildrenList = oldValueNode.getValue(); for (int i = 0, listSize = newValueNode.getTupleSize(); i < listSize; i++) { if (currentChildrenList.get(i) == oldChild) { TypeExpr childType = newValueNode.getValueAt(i).getTypeExpr(); newValueNode.setValueNodeAt(i, newChild.copyValueNode(childType)); break; } } // Set the value node replaceValueNode(newValueNode, true); } }