/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.editors.layout.parts; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener; import com.android.ide.eclipse.editors.uimodel.UiElementNode; import com.android.sdklib.SdkConstants; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; 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.commands.Command; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.gef.editpolicies.LayoutEditPolicy; import org.eclipse.gef.editpolicies.SelectionEditPolicy; import org.eclipse.gef.requests.CreateRequest; import org.eclipse.gef.requests.DropRequest; import org.eclipse.gef.tools.SelectEditPartTracker; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import java.util.List; /** * An {@link EditPart} for a {@link UiElementNode}. */ public abstract class UiElementEditPart extends AbstractGraphicalEditPart implements IUiUpdateListener { public UiElementEditPart(UiElementNode uiElementNode) { setModel(uiElementNode); } //------------------------- // Derived classes must define these abstract protected void hideSelection(); abstract protected void showSelection(); //------------------------- // Base class overrides @Override public DragTracker getDragTracker(Request request) { return new SelectEditPartTracker(this); } @Override protected void createEditPolicies() { /* * This is no longer needed, as a selection edit policy is set by the parent layout. * Leave this code commented out right now, I'll want to play with this later. * installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new NonResizableSelectionEditPolicy(this)); */ } /* (non-javadoc) * Returns a List containing the children model objects. * Must not return null, instead use the super which returns an empty list. */ @SuppressWarnings("unchecked") @Override protected List getModelChildren() { return getUiNode().getUiChildren(); } @Override public void activate() { super.activate(); getUiNode().addUpdateListener(this); } @Override public void deactivate() { super.deactivate(); getUiNode().removeUpdateListener(this); } @Override protected void refreshVisuals() { if (getFigure().getParent() != null) { ((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), getBounds()); } // update the visuals of the children as well refreshChildrenVisuals(); } protected void refreshChildrenVisuals() { if (children != null) { for (Object child : children) { if (child instanceof UiElementEditPart) { UiElementEditPart childPart = (UiElementEditPart)child; childPart.refreshVisuals(); } } } } //------------------------- // IUiUpdateListener implementation public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { // TODO: optimize by refreshing only when needed switch(state) { case ATTR_UPDATED: refreshVisuals(); break; case CHILDREN_CHANGED: refreshChildren(); // new children list, need to update the layout refreshVisuals(); break; case CREATED: refreshVisuals(); break; case DELETED: // pass break; } } //------------------------- // Local methods /** @return The object model casted to an {@link UiElementNode} */ public final UiElementNode getUiNode() { return (UiElementNode) getModel(); } protected final ElementDescriptor getDescriptor() { return getUiNode().getDescriptor(); } protected final UiElementEditPart getEditPartParent() { EditPart parent = getParent(); if (parent instanceof UiElementEditPart) { return (UiElementEditPart)parent; } return null; } /** * Returns a given XML attribute. * @param attrName The local name of the attribute. * @return the attribute as a {@link String}, if it exists, or <code>null</code> */ protected final String getStringAttr(String attrName) { UiElementNode uiNode = getUiNode(); if (uiNode.getXmlNode() != null) { Node xmlNode = uiNode.getXmlNode(); if (xmlNode != null) { NamedNodeMap nodeAttributes = xmlNode.getAttributes(); if (nodeAttributes != null) { Node attr = nodeAttributes.getNamedItemNS( SdkConstants.NS_RESOURCES, attrName); if (attr != null) { return attr.getNodeValue(); } } } } return null; } protected final Rectangle getBounds() { UiElementNode model = (UiElementNode)getModel(); Object editData = model.getEditData(); if (editData != null) { // assert with fully qualified class name to prevent import changes to another // Rectangle class. assert (editData instanceof org.eclipse.draw2d.geometry.Rectangle); return (Rectangle)editData; } // return a dummy rect return new Rectangle(0, 0, 0, 0); } /** * Returns the EditPart that should be used as the target for the specified Request. * <p/> * For instance this is called during drag'n'drop with a CreateRequest. * <p/> * Reject being a target for elements which descriptor does not allow children. * * {@inheritDoc} */ @Override public EditPart getTargetEditPart(Request request) { if (request != null && request.getType() == RequestConstants.REQ_CREATE) { // Reject being a target for elements which descriptor does not allow children. if (!getUiNode().getDescriptor().hasChildren()) { return null; } } return super.getTargetEditPart(request); } /** * Used by derived classes {@link UiDocumentEditPart} and {@link UiLayoutEditPart} * to accept drag'n'drop of new items from the palette. * * @param layoutEditPart The layout edit part where this policy is installed. It can * be either a {@link UiDocumentEditPart} or a {@link UiLayoutEditPart}. */ protected void installLayoutEditPolicy(final UiElementEditPart layoutEditPart) { // This policy indicates how elements can be constrained by the layout. // TODO Right now we use the XY layout policy since our constraints are // handled by the android rendering engine rather than GEF. Tweak as // appropriate. installEditPolicy(EditPolicy.LAYOUT_ROLE, new LayoutEditPolicy() { /** * We don't allow layout children to be resized yet. * <p/> * Typical choices would be: * <ul> * <li> ResizableEditPolicy, to allow for selection, move and resize. * <li> NonResizableEditPolicy, to allow for selection, move but not resize. * <li> SelectionEditPolicy to allow for only selection. * </ul> * <p/> * TODO: make this depend on the part layout. For an AbsoluteLayout we should * probably use a NonResizableEditPolicy and SelectionEditPolicy for the rest. * Whether to use ResizableEditPolicy or NonResizableEditPolicy should depend * on the child in an AbsoluteLayout. */ @Override protected EditPolicy createChildEditPolicy(EditPart child) { if (child instanceof UiElementEditPart) { return new NonResizableSelectionEditPolicy((UiElementEditPart) child); } return null; } @Override protected Command getCreateCommand(CreateRequest request) { // We store the ElementDescriptor in the request.factory.type Object newType = request.getNewObjectType(); if (newType instanceof ElementDescriptor) { Point where = request.getLocation().getCopy(); Point origin = getLayoutContainer().getClientArea().getLocation(); where.translate(origin.getNegated()); // The host is the EditPart where this policy is installed, // e.g. this UiElementEditPart. EditPart host = getHost(); if (host instanceof UiElementEditPart) { return new ElementCreateCommand((ElementDescriptor) newType, (UiElementEditPart) host, where); } } return null; } @Override protected Command getMoveChildrenCommand(Request request) { // TODO Auto-generated method stub return null; } @Override public void showLayoutTargetFeedback(Request request) { super.showLayoutTargetFeedback(request); // for debugging // System.out.println("target: " + request.toString() + " -- " + layoutEditPart.getUiNode().getBreadcrumbTrailDescription(false)); if (layoutEditPart instanceof UiLayoutEditPart && request instanceof DropRequest) { Point where = ((DropRequest) request).getLocation().getCopy(); Point origin = getLayoutContainer().getClientArea().getLocation(); where.translate(origin.getNegated()); ((UiLayoutEditPart) layoutEditPart).showDropTarget(where); } } @Override protected void eraseLayoutTargetFeedback(Request request) { super.eraseLayoutTargetFeedback(request); if (layoutEditPart instanceof UiLayoutEditPart) { ((UiLayoutEditPart) layoutEditPart).hideDropTarget(); } } @Override protected IFigure createSizeOnDropFeedback(CreateRequest createRequest) { // TODO understand if this is useful for us or remove return super.createSizeOnDropFeedback(createRequest); } }); } protected static class NonResizableSelectionEditPolicy extends SelectionEditPolicy { private final UiElementEditPart mEditPart; public NonResizableSelectionEditPolicy(UiElementEditPart editPart) { mEditPart = editPart; } @Override protected void hideSelection() { mEditPart.hideSelection(); } @Override protected void showSelection() { mEditPart.showSelection(); } } }