/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.diagram.ui.notation.uml.part; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.draw2d.Button; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Label; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.AccessibleAnchorProvider; import org.eclipse.gef.DragTracker; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.editpolicies.NonResizableEditPolicy; import org.eclipse.gef.tools.DirectEditManager; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.diagram.ui.DiagramUiConstants; import org.teiid.designer.diagram.ui.actions.ScaledFont; import org.teiid.designer.diagram.ui.editor.DiagramController; import org.teiid.designer.diagram.ui.editor.DiagramEditor; import org.teiid.designer.diagram.ui.editor.DiagramEditorUtil; import org.teiid.designer.diagram.ui.editor.DiagramViewer; import org.teiid.designer.diagram.ui.figure.ExpandableFigure; import org.teiid.designer.diagram.ui.model.DiagramModelNode; import org.teiid.designer.diagram.ui.notation.uml.figure.UmlClassifierContainerFigure; import org.teiid.designer.diagram.ui.notation.uml.figure.UmlClassifierFigure; import org.teiid.designer.diagram.ui.notation.uml.model.UmlClassifierContainerNode; import org.teiid.designer.diagram.ui.notation.uml.model.UmlClassifierNode; import org.teiid.designer.diagram.ui.part.AbstractNotationEditPart; import org.teiid.designer.diagram.ui.part.DiagramEditPart; import org.teiid.designer.diagram.ui.part.ExpandableDiagram; import org.teiid.designer.diagram.ui.part.PropertyChangeManager; import org.teiid.designer.diagram.ui.util.DiagramUiUtilities; import org.teiid.designer.diagram.ui.util.HiliteDndNodeSelectionEditPolicy; import org.teiid.designer.diagram.ui.util.SelectionTracker; import org.teiid.designer.diagram.ui.util.directedit.DirectEditFigure; import org.teiid.designer.diagram.ui.util.directedit.DirectEditPart; import org.teiid.designer.diagram.ui.util.directedit.DirectEditPartEditPolicy; import org.teiid.designer.diagram.ui.util.directedit.DirectEditPartManager; import org.teiid.designer.diagram.ui.util.directedit.LabelCellEditorLocator; import org.teiid.designer.metamodels.diagram.Diagram; import org.teiid.designer.ui.editors.ModelEditorManager; import org.teiid.designer.ui.event.IRevealHideListener; import org.teiid.designer.ui.viewsupport.ModelObjectUtilities; /** * UmlClassifierEditPart * * @since 8.0 */ public class UmlClassifierEditPart extends AbstractNotationEditPart implements DirectEditPart { // =========================================================================================================================== // Constants private static final int ARROW_BORDER_5 = 5; // Pixels around collapse arrow that will be still be selectable private static final int ARROW_BORDER_8 = 8; // Pixels around collapse arrow that will be still be selectable // =========================================================================================================================== // Variables private DragTracker myDragTracker = null; private DirectEditManager manager; private IRevealHideListener revealHideListener; // =========================================================================================================================== // Constructors public UmlClassifierEditPart() { super(); } public UmlClassifierEditPart( String diagramTypeId ) { super(); setDiagramTypeId(diagramTypeId); init(); } // =========================================================================================================================== // Methods /* * Classifier should only show arrow if it can collapse and that is determined by it's parent. This will solve the problem of * nested classifieres too. */ private boolean canCollapse() { boolean bResult = (this.getParent() instanceof ExpandableDiagram); return bResult; } private boolean clickedOnArrow() { if (getDiagramFigure() instanceof ExpandableFigure) { SelectionTracker tracker = (SelectionTracker)getDragTracker(null); if (tracker != null && tracker.getLastMouseLocation() != null) { DiagramViewer dvViewer = null; // jh Fix for Defect 21054 // since getViewer is documented to throw an NPE, catch it and // treat it as a false condition try { dvViewer = (DiagramViewer)getViewer(); } catch (NullPointerException npe) { return false; } if (dvViewer == null) { return false; } DiagramEditor deEditor = dvViewer.getEditor(); double zoomFactor = deEditor.getCurrentZoomFactor(); Point rawMousePoint = new Point(tracker.getLastMouseLocation()); Point mousePoint = new Point(rawMousePoint.x / zoomFactor, rawMousePoint.y / zoomFactor); Point rawViewportLoc = ((DiagramViewer)getViewer()).getViewportLocation(); Point viewportLoc = new Point(rawViewportLoc.x / zoomFactor, rawViewportLoc.y / zoomFactor); // Let's get the rectangle for the figure for the name in the header... ExpandableFigure cFigure = (ExpandableFigure)getDiagramFigure(); IFigure arrowFigure = cFigure.getExpansionFigure(); // get x,y offset for nested classifier if (arrowFigure != null) { // Get name figure bounds and correct for scrolling and classifier nesting Rectangle arrowRect = new Rectangle(arrowFigure.getBounds()); arrowRect.x = arrowRect.x + ((IFigure)cFigure).getBounds().x - ARROW_BORDER_5 - viewportLoc.x; arrowRect.y = arrowRect.y + ((IFigure)cFigure).getBounds().y - ARROW_BORDER_5 - viewportLoc.y; // Let's add a coupleof pixels on the bounds arrowRect.width += ARROW_BORDER_5; arrowRect.height += ARROW_BORDER_8; if (arrowRect.contains(mousePoint)) { return true; } } } } return false; } public void collapse() { if (canCollapse()) { // must grab the child EObjects before we execute the collapse List lstChildEObjects = getChildEObjects(); // System.out.println("[UmlClassifierEditPart.collapse] about to call collapse on: " + getDiagramFigure() ); ((UmlClassifierFigure)getDiagramFigure()).collapse(); refresh(); layout(true); // if in a MappingClassDiagram, synchronize with the tree if (getRevealHideListener() != null && getRevealHideListener().isRevealHideBehaviorEnabled() && lstChildEObjects != null && lstChildEObjects != Collections.EMPTY_LIST) { // get the list of newly revealed EObjects // announce them to the reveal listener getRevealHideListener().notifyElementsHidden(this, lstChildEObjects); } } } /** * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies() You need to tell how children nodes will be layed * out... */ @Override protected void createEditPolicies() { setPrimaryParent(true); installEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE, new NonResizableEditPolicy()); installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new HiliteDndNodeSelectionEditPolicy()); installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new DirectEditPartEditPolicy()); } /** * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure() */ @Override protected IFigure createFigure() { Point posn = new Point(100, 100); UmlClassifierFigure newFigure = (UmlClassifierFigure)getFigureGenerator().createFigure(getModel()); UmlClassifierNode model = (UmlClassifierNode)getModel(); if (model.getParent() instanceof ExpandableDiagram && ((ExpandableDiagram)model.getParent()).canExpand()) { newFigure.setExpandable(true); } newFigure.setLocation(posn); Image firstOverlayImage = model.getFirstOverlayImage(); Image secondOverlayImage = model.getSecondOverlayImage(); if (firstOverlayImage != null) { newFigure.addEditButton(firstOverlayImage); } if (secondOverlayImage != null) { newFigure.addImage(secondOverlayImage, DiagramUiConstants.Position.UPPER_RIGHT); } // let's check to see if the name is italisized? int nameFontStyle = ScaledFont.BOLD_STYLE; if (model.isAbstract()) { nameFontStyle = ScaledFont.BOLD_ITALICS_STYLE; } newFigure.setNameFontStyle(nameFontStyle); return newFigure; } public boolean doubleClickedName() { SelectionTracker tracker = (SelectionTracker)getDragTracker(null); if (tracker != null && tracker.getLastMouseLocation() != null) { double zoomFactor = ((DiagramViewer)getViewer()).getEditor().getCurrentZoomFactor(); Point rawMousePoint = new Point(tracker.getLastMouseLocation()); Point mousePoint = new Point(rawMousePoint.x / zoomFactor, rawMousePoint.y / zoomFactor); Point rawViewportLoc = ((DiagramViewer)getViewer()).getViewportLocation(); Point viewportLoc = new Point(rawViewportLoc.x / zoomFactor, rawViewportLoc.y / zoomFactor); // Let's get the rectangle for the figure for the name in the header... UmlClassifierFigure cFigure = (UmlClassifierFigure)getDiagramFigure(); IFigure nameFigure = cFigure.getNameFigure(); // get x,y offset for nested classifier Point nestedOffset = DiagramUiUtilities.getNestedRelativeLocation(this); if (nameFigure != null) { // Get name figure bounds and correct for scrolling and classifier nesting Rectangle nameRect = new Rectangle(nameFigure.getBounds()); nameRect.x = nameRect.x + cFigure.getBounds().x - 3 - viewportLoc.x + nestedOffset.x; nameRect.y = nameRect.y + cFigure.getBounds().y - 3 - viewportLoc.y + nestedOffset.y; // Let's add a coupleof pixels on the bounds nameRect.width += 6; nameRect.height += 6; if (nameRect.contains(mousePoint)) { return true; } } } return false; } /** * @see org.teiid.designer.diagram.ui.part.EditableEditPart#edit() */ @Override public void edit() { if (!clickedOnArrow()) { if (doubleClickedName()) { if (getSelectionHandler().shouldRename(getModelObject())) { if (ModelerCore.getModelEditor().hasName(getModelObject()) && !ModelObjectUtilities.isReadOnly(getModelObject())) performDirectEdit(); } } else { // Let's delegate to the modelEditorManager here ModelEditorManager.edit(getModelObject()); } } } public void expand() { if (canCollapse()) { // System.out.println("[UmlClassifierEditPart.collapse] about to call expand on: " + this ); ((UmlClassifierFigure)getDiagramFigure()).expand(); refresh(); layout(true); Dimension newSize = getFigure().getSize(); ((DiagramModelNode)getModel()).setSize(newSize); // if in a MappingClassDiagram, synchronize with the tree if (getRevealHideListener() != null && getRevealHideListener().isRevealHideBehaviorEnabled()) { // announce them to the reveal listener getRevealHideListener().notifyElementsRevealed(this, getChildEObjects()); } } } @Override public Object getAdapter( Class key ) { if (key == AccessibleAnchorProvider.class) return new DefaultAccessibleAnchorProvider() { @Override public List getSourceAnchorLocations() { List list = new ArrayList(); Dimension thisSize = getFigure().getSize(); list.add(new Point(0, 0)); list.add(new Point(thisSize.width / 2, 0)); list.add(new Point(thisSize.width, 0)); list.add(new Point(thisSize.width, thisSize.height / 2)); list.add(new Point(thisSize.width, thisSize.height)); list.add(new Point(thisSize.width / 2, thisSize.height)); list.add(new Point(0, thisSize.height)); list.add(new Point(0, thisSize.height / 2)); return list; } @Override public List getTargetAnchorLocations() { List list = new ArrayList(); Dimension thisSize = getFigure().getSize(); list.add(new Point(0, 0)); list.add(new Point(thisSize.width / 2, 0)); list.add(new Point(thisSize.width, 0)); list.add(new Point(thisSize.width, thisSize.height / 2)); list.add(new Point(thisSize.width, thisSize.height)); list.add(new Point(thisSize.width / 2, thisSize.height)); list.add(new Point(0, thisSize.height)); list.add(new Point(0, thisSize.height / 2)); return list; } }; return super.getAdapter(key); } private List getChildEObjects() { List lstChildren = Collections.EMPTY_LIST; ArrayList arylEObjects = new ArrayList(); if (getChildren() != null && !getChildren().isEmpty()) { UmlClassifierContainerEditPart container = (UmlClassifierContainerEditPart)getChildren().get(0); lstChildren = container.getChildren(); Iterator it = lstChildren.iterator(); while (it.hasNext()) { Object oTemp = it.next(); if (oTemp instanceof UmlAttributeEditPart) { UmlAttributeEditPart attr = (UmlAttributeEditPart)oTemp; // System.out.println("[UmlClassifierEditPart.getChildEObjects] a child ModelObject: " + attr.getModelObject() // ); arylEObjects.add(attr.getModelObject()); } } lstChildren = arylEObjects; } return lstChildren; } /** * This method is not mandatory to implement, but if you do not implement it, you will not have the ability to * rectangle-selects several figures... */ @Override public DragTracker getDragTracker( Request req ) { // Unlike in Logical Diagram Editor example, I use a singleton because this // method is Entered >> several time, so I prefer to save memory ; and it works! if (myDragTracker == null) { myDragTracker = new SelectionTracker(this); } return myDragTracker; } /** * @see org.teiid.designer.diagram.ui.util.directedit.DirectEditPart#getEditManager() */ @Override public DirectEditPartManager getEditManager() { return (DirectEditPartManager)manager; } @Override public String getEditString() { return ((DiagramModelNode)getModel()).getName(); } private Label getLabel() { Label label = null; if (getFigure() instanceof DirectEditFigure) { label = ((DirectEditFigure)getFigure()).getLabelFigure(); } return label; } public IRevealHideListener getRevealHideListener() { if (revealHideListener == null) { DiagramController deController = ((DiagramViewer)getViewer()).getEditor().getDiagramController(); if (deController instanceof IRevealHideListener) { revealHideListener = (IRevealHideListener)deController; } } return revealHideListener; } @Override public String getText() { return ((DiagramModelNode)getModel()).getName(); } /** * @see org.teiid.designer.diagram.ui.part.DiagramEditPart#hiliteBackground(org.eclipse.swt.graphics.Color) */ @Override public void hiliteBackground( Color hiliteColor ) { getDiagramFigure().hiliteBackground(hiliteColor); } public void init() { if (getAnchorManager() == null) { setAnchorManager(getEditPartFactory().getAnchorManager(this)); } } /** * @see org.teiid.designer.diagram.ui.part.DiagramEditPart#layout(boolean) */ @Override public void layout( boolean layoutChildren ) { // Need to set the stack order of the containers.... // Get the children of this edit part // If container edit parts, get the model nodes resetContainerStackIndex(); super.layout(layoutChildren); } @Override public void performDirectEdit() { if (manager == null) manager = new DirectEditPartManager(this, TextCellEditor.class, new LabelCellEditorLocator(getLabel())); manager.show(); } @Override public void performRequest( Request request ) { if (request.getType() == RequestConstants.REQ_DIRECT_EDIT) { if (!(getSelectionHandler().handleDoubleClick(this.getModelObject()))) { if (ModelerCore.getModelEditor().hasName(getModelObject())) performDirectEdit(); } } else if (request.getType() == RequestConstants.REQ_SELECTION && canCollapse()) { if (clickedOnArrow()) { if (((UmlClassifierNode)getModel()).isExpanded()) ((UmlClassifierNode)getModel()).collapse(); else ((UmlClassifierNode)getModel()).expand(); } } } /** * @see java.beans.PropertyChangeListener#propertyChange(PropertyChangeEvent) */ @Override public void propertyChange( PropertyChangeEvent evt ) { // boolean refresh = false; // String prop = evt.getPropertyName(); super.propertyChange(evt); if (prop.equals(DiagramUiConstants.DiagramNodeProperties.LAYOUT)) { getChangeManager().refresh(PropertyChangeManager.LAYOUT_ALL, false); } else if (prop.equals(DiagramUiConstants.DiagramNodeProperties.SIZE)) { getChangeManager().refresh(PropertyChangeManager.RESIZE_CHILDREN, false); getChangeManager().refresh(PropertyChangeManager.GENERAL, false); if (!((DiagramModelNode)getModel()).getSourceConnections().isEmpty() || !((DiagramModelNode)getModel()).getTargetConnections().isEmpty()) { ((DiagramModelNode)getModel()).updateAssociations(); } getChangeManager().refresh(PropertyChangeManager.LABELS, false); } else if (prop.equals(DiagramUiConstants.DiagramNodeProperties.IMAGES)) { refreshImages(); } else if (prop.equals(DiagramUiConstants.DiagramNodeProperties.CONNECTION)) { getChangeManager().refresh(PropertyChangeManager.SOURCE_CONNECTIONS, false); getChangeManager().refresh(PropertyChangeManager.TARGET_CONNECTIONS, false); getChangeManager().refresh(PropertyChangeManager.ANCHORS, false); getChangeManager().refresh(PropertyChangeManager.GENERAL, false); getChangeManager().refresh(PropertyChangeManager.LABELS, false); } else if (prop.equals(DiagramUiConstants.DiagramNodeProperties.LOCATION)) { if (!((DiagramModelNode)getModel()).getSourceConnections().isEmpty() || !((DiagramModelNode)getModel()).getTargetConnections().isEmpty()) { ((DiagramModelNode)getModel()).updateAssociations(); } getChangeManager().refresh(PropertyChangeManager.LABELS, false); } else if (prop.equals(DiagramUiConstants.DiagramNodeProperties.IMAGES)) { refreshImages(); } if (prop.equals(DiagramUiConstants.DiagramNodeProperties.PATH)) { refreshPath(); getChangeManager().refresh(PropertyChangeManager.RESIZE_CHILDREN, false); } // If parent is ExpandableDiagram if (canCollapse()) { if (prop.equals(DiagramUiConstants.DiagramNodeProperties.COLLAPSE)) { // Let's control construction refresh() Diagram diagram = ((DiagramModelNode)this.getModel()).getDiagram(); boolean handleConstruction = !DiagramEditorUtil.isDiagramUnderConstruction(diagram); if (handleConstruction) { DiagramEditorUtil.setDiagramUnderConstruction(diagram); } collapse(); getChangeManager().refresh(PropertyChangeManager.VISUALS, false); getChangeManager().refresh(PropertyChangeManager.ANCHORS, false); if (handleConstruction) { DiagramEditorUtil.setDiagramConstructionComplete(diagram, true); } } if (prop.equals(DiagramUiConstants.DiagramNodeProperties.EXPAND)) { Diagram diagram = ((DiagramModelNode)this.getModel()).getDiagram(); boolean handleConstruction = !DiagramEditorUtil.isDiagramUnderConstruction(diagram); if (handleConstruction) { DiagramEditorUtil.setDiagramUnderConstruction(diagram); } expand(); getChangeManager().refresh(PropertyChangeManager.VISUALS, false); getChangeManager().refresh(PropertyChangeManager.ANCHORS, false); if (handleConstruction) { DiagramEditorUtil.setDiagramConstructionComplete(diagram, true); } } } } public void refreshImages() { DiagramModelNode dmn = (DiagramModelNode)getModel(); if (dmn != null) { UmlClassifierFigure figure = (UmlClassifierFigure)getDiagramFigure(); figure.addImage(dmn.getSecondOverlayImage(), DiagramUiConstants.Position.UPPER_RIGHT); Button button = figure.getEditButton(); if (button != null) { for (Iterator iter = button.getChildren().iterator(); iter.hasNext();) { Object obj = iter.next(); if (obj instanceof Label) { ((Label)obj).setIcon(dmn.getFirstOverlayImage()); break; } } } layout(true); } } /** * @See org.teiid.designer.diagram.ui.DiagramEditPart#resizeChildren() */ @Override public void refreshName() { // Now we need to update the abstract title.... // let's check to see if the name is italisized? int nameFontStyle = ScaledFont.BOLD_STYLE; if (((UmlClassifierNode)getModel()).isAbstract()) { nameFontStyle = ScaledFont.BOLD_ITALICS_STYLE; } ((UmlClassifierFigure)getDiagramFigure()).setNameFontStyle(nameFontStyle); // Need to get the figure and update the name super.refreshName(); } public void refreshPath() { ((UmlClassifierFigure)getDiagramFigure()).updateForPath(((UmlClassifierNode)getModel()).getPath()); } /** * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals() */ @Override protected void refreshVisuals() { Point loc = ((DiagramModelNode)getModel()).getPosition(); Dimension size = ((DiagramModelNode)getModel()).getSize(); Rectangle r = new Rectangle(loc, size); ((GraphicalEditPart)getParent()).setLayoutConstraint(this, getFigure(), r); getFigure().repaint(); } private void resetContainerStackIndex() { Iterator iter = getChildren().iterator(); EditPart nextChild = null; while (iter.hasNext()) { nextChild = (EditPart)iter.next(); if (nextChild instanceof UmlClassifierContainerEditPart) { UmlClassifierContainerNode cn = (UmlClassifierContainerNode)((DiagramEditPart)nextChild).getModel(); UmlClassifierContainerFigure cf = (UmlClassifierContainerFigure)((DiagramEditPart)nextChild).getDiagramFigure(); cf.setStackOrderValue(cn.getType()); } } } /** * @see org.teiid.designer.diagram.ui.part.DiagramEditPart#resizeChildren() */ @Override public void resizeChildren() { getDiagramFigure().updateForSize(((DiagramModelNode)getModel()).getSize()); } public void setRevealHideListener( IRevealHideListener listener ) { this.revealHideListener = listener; } @Override public void setText( String newName ) { ((DiagramModelNode)getModel()).setName(newName); } /** * @see org.teiid.designer.diagram.ui.part.DiagramEditPart#shouldHiliteBackground() */ @Override public boolean shouldHiliteBackground( List sourceEditParts ) { // We'll start off by checking to see that list of sourceEditParts do not have the same parent as this. return true; } }