/******************************************************************************* * Copyright (c) 2006 Sybase, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sybase, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.jst.pagedesigner.parts; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Point; 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.requests.LocationRequest; import org.eclipse.gef.tools.DragEditPartsTracker; import org.eclipse.jst.jsf.common.ui.internal.logging.Logger; import org.eclipse.jst.jsf.core.internal.tld.CMUtil; import org.eclipse.jst.pagedesigner.PDPlugin; import org.eclipse.jst.pagedesigner.converter.ConvertPosition; import org.eclipse.jst.pagedesigner.converter.IConverterFactory; import org.eclipse.jst.pagedesigner.converter.ITagConverter; import org.eclipse.jst.pagedesigner.css2.ICSSStyle; import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; import org.eclipse.jst.pagedesigner.css2.layout.CSSWidgetLayout; import org.eclipse.jst.pagedesigner.css2.style.AbstractStyle; import org.eclipse.jst.pagedesigner.css2.widget.HiddenProvider; import org.eclipse.jst.pagedesigner.dtmanager.DTManager; import org.eclipse.jst.pagedesigner.editpolicies.ElementMenuBar; import org.eclipse.jst.pagedesigner.editpolicies.ElementResizableEditPolicy; import org.eclipse.jst.pagedesigner.editpolicies.IEnhancedSelectionEditPolicy; import org.eclipse.jst.pagedesigner.elementedit.ElementEditFactoryRegistry; import org.eclipse.jst.pagedesigner.elementedit.IElementEdit; import org.eclipse.jst.pagedesigner.figurehandler.FigureFactory; import org.eclipse.jst.pagedesigner.figurehandler.IFigureHandler; import org.eclipse.jst.pagedesigner.jsp.core.IJSPCoreConstants; import org.eclipse.jst.pagedesigner.range.RangeUtil; import org.eclipse.jst.pagedesigner.requests.PageDesignerRequestConstants; import org.eclipse.jst.pagedesigner.viewer.DesignRange; import org.eclipse.jst.pagedesigner.viewer.IHTMLGraphicalViewer; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Image; import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * @author mengbo * @version 1.5 */ public class ElementEditPart extends SubNodeEditPart { private static Logger _log = PDPlugin.getLogger(ElementEditPart.class); private Element _elementNode; private ITagConverter _tagConverter; private ElementMenuBar _nonVisualElementBar; /* * (non-Javadoc) * * @see org.eclipse.gef.EditPart#setModel(java.lang.Object) */ public void setModel(Object model) { super.setModel(model); _elementNode = (Element) model; _tagConverter = getTagConverter(_elementNode); _tagConverter.convertRefresh(null); adaptEditProxy(); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.parts.NodeEditPart#getDragTracker(org.eclipse.gef.Request) */ public DragTracker getDragTracker(Request request) { EditPolicy policy = this .getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE); if (PageDesignerRequestConstants.REQ_SELECTION_TRACKER.equals(request.getType()) || org.eclipse.gef.RequestConstants.REQ_SELECTION.equals(request.getType())) { if (policy instanceof IEnhancedSelectionEditPolicy && request instanceof LocationRequest) { return ((IEnhancedSelectionEditPolicy)policy).getSelectionDragTracker((LocationRequest)request); } return null; } // should not happen return new DragEditPartsTracker(this); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.parts.NodeEditPart#createEditPolicies() */ protected void createEditPolicies() { super.createEditPolicies(); IElementEdit support = getElementEdit(); if (support != null) { support.createEditPolicies(this); } // if ElementEdit didn't install special SELECTION_FEEDBACK_ROLE policy, // then default if (this.getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE) == null) { this.installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new ElementResizableEditPolicy()); } } /** * @return the associated element edit */ public IElementEdit getElementEdit() { // XXX: should we cache it? return ElementEditFactoryRegistry.getInstance().createElementEdit( _elementNode); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.parts.NodeEditPart#addNotify() */ public void addNotify() { if (_tagConverter == null) { _tagConverter = getTagConverter(_elementNode); _tagConverter.convertRefresh(null); adaptEditProxy(); } super.addNotify(); } /** * @param node * @return */ private ITagConverter getTagConverter(Element node) { return DTManager.getInstance().getTagConverter(node, IConverterFactory.MODE_DESIGNER, this.getDestDocumentForDesign()); } /* * (non-Javadoc) * * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#removeNotify() */ public void removeNotify() { super.removeNotify(); // if (_tagConverter != null) // { // _tagConverter.dispose(); // _tagConverter = null; // } } /* * (non-Javadoc) * * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren() */ protected List getModelChildren() { List children_ = new ArrayList(_tagConverter.getChildModeList()); for (Iterator it = _tagConverter.getNonVisualChildren().iterator(); it.hasNext();) { Element nonVisualChild = (Element) it.next(); children_.add(DTManager.getInstance().getTagConverter(nonVisualChild, IConverterFactory.MODE_DESIGNER, this.getDestDocumentForDesign())); } return children_; } /* * (non-Javadoc) * * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure() */ protected IFigure createFigure() { // if (_tagConverter.isVisualByHTML()) // { // Element result = _tagConverter.getResultElement(); // return FigureFactory.createFigure(result, // true);//_tagConverter.isMultiLevel()); // } // else // { // CSSWidgetFigure figure = new CSSWidgetFigure(this._elementNode, // createHiddenProvider()); // return figure; // } return new CSSFigure(); } /** * @return */ private HiddenProvider createHiddenProvider() { Element result = _tagConverter.getHostElement(); String localName = result.getLocalName(); String appendString = localName; if (localName.equalsIgnoreCase(IJSPCoreConstants.TAG_DIRECTIVE_TAGLIB)) { appendString = ((IDOMElement) result) .getAttribute(IJSPCoreConstants.ATTR_URI); if (appendString == null) { appendString = ((IDOMElement) result) .getAttribute(IJSPCoreConstants.ATTR_TAGDIR); if (appendString == null) appendString = ""; //$NON-NLS-1$ } } Image image = _tagConverter.getVisualImage(); HiddenProvider provider = new HiddenProvider(image, this); ((CSSFigure) getFigure()).setCSSStyle(provider.getCSSStyle()); provider.setLabel(appendString); return provider; } /** * called by the * @param recursive * */ public void refreshModelChange(boolean recursive) { IElementEdit support = getElementEdit(); if (support == null || !support.handleModelChange(_elementNode, this, recursive)) { this.refresh(recursive); } } /* * (non-Javadoc) * * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#refresh() */ public void refresh() { refresh(false); } /** * @param recursive */ public void refresh(boolean recursive) { if (!_tagConverter.isVisualByHTML()) { _tagConverter.convertRefresh(null); ((CSSFigure) getFigure()) .setFixedLayoutManager(new CSSWidgetLayout( (CSSFigure) getFigure(), createHiddenProvider())); // nothing to refresh // ((CSSWidgetFigure) // getFigure()).setProvider(createHiddenProvider()); return; } EditPart editPart; Object model; Map modelToEditPart = new HashMap(); List children1 = getChildren(); for (int i = 0, n = children1.size(); i < n; i++) { editPart = (EditPart) children1.get(i); modelToEditPart.put(editPart.getModel(), editPart); // remove child visual, since we may reconstruct the figure // structure of this edit part removeChildVisual(editPart); } Element oldEle = _tagConverter.getResultElement(); // link parent node. Node parent = oldEle.getParentNode(); _tagConverter.convertRefresh(null); if (parent != null) { // a new element is generated. replace the old one. parent.replaceChild(_tagConverter.getResultElement(), oldEle); } adaptEditProxy(); // XXX: comment out the if-else for always deep update. // this is for the case when a empty container generate child // text node, and then when user input data into the container, // the node change from "multiLevel" state to "non-multilevel" // state. We don't handle this very well yet, so always to deep // update for now. (lium) // if (_tagConverter.isMultiLevel()) // { FigureFactory.updateDeepFigure(_tagConverter.getResultElement(), oldEle, (CSSFigure) this.getFigure()); // } // else // { // FigureFactory.updateNonDeepFigure(_tagConverter.getResultElement(), // this.getFigure()); // } List modelObjects = getModelChildren(); if (!recursive) { for (int i = 0, n = modelObjects.size(); i < n; i++) { model = modelObjects.get(i); // Look to see if the EditPart is already around but in the // wrong location editPart = (EditPart) modelToEditPart.remove(model); if (editPart != null) { addChildVisual(editPart, i); } else { // An editpart for this model doesn't exist yet. Create and // insert one. editPart = createChild(model); addChild(editPart, i); } } for (Iterator iter = modelToEditPart.values().iterator(); iter .hasNext();) { EditPart part = (EditPart) iter.next(); removeChild(part); } } else { // remove all child, and recreate them. for (Iterator iter = modelToEditPart.values().iterator(); iter .hasNext();) { EditPart part = (EditPart) iter.next(); removeChild(part); } for (int i = 0, n = modelObjects.size(); i < n; i++) { model = modelObjects.get(i); // Look to see if the EditPart is already around but in the // wrong location // An editpart for this model doesn't exist yet. Create and // insert one. editPart = createChild(model); addChild(editPart, i); } } } /** * */ private void adaptEditProxy() { Element resultEle = _tagConverter.getResultElement(); if (resultEle instanceof IDOMElement) { INodeAdapter adapter = ((IDOMElement) resultEle) .getAdapterFor(EditProxyAdapter.class); if (adapter != null) { ((IDOMElement) resultEle).removeAdapter(adapter); } ((IDOMElement) resultEle).addAdapter(new EditProxyAdapter(this)); } } /** * @return true if we are in range mode and this is in * the selection range */ public boolean isRangeSelected() { IHTMLGraphicalViewer viewer = (IHTMLGraphicalViewer) this.getViewer(); if (viewer == null || !viewer.isInRangeMode()) { return false; } DesignRange range = viewer.getRangeSelection(); if (range == null || !range.isValid()) { return false; } return RangeUtil.intersect(range, this); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.parts.NodeEditPart#isWidget() */ public boolean isWidget() { return _tagConverter.isWidget(); } /** * @return true if our model node can have direct text children */ public boolean canHaveDirectTextChild() { return CMUtil.canHaveDirectTextChild(this._elementNode); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.parts.NodeEditPart#isResizable() */ public boolean isResizable() { if (!_tagConverter.isVisualByHTML()) { return false; } IElementEdit edit = this.getElementEdit(); if (edit != null) { return edit.isResizable(this._elementNode); } CMElementDeclaration decl = CMUtil .getElementDeclaration(this._elementNode); if (decl != null) { // XXX: default implementation, if this element support "style" // attribute, // then we think it support resize. return decl.getAttributes().getNamedItem("style") != null; //$NON-NLS-1$ } return true; } /** * @param parent * @return */ private IFigure getFigure(Node parent) { if (parent instanceof INodeNotifier) { IFigureHandler handler = (IFigureHandler) ((INodeNotifier) parent) .getAdapterFor(IFigureHandler.class); if (handler != null) { return handler.getFigure(); } } return null; } /* * (non-Javadoc) * * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#addChildVisual(org.eclipse.gef.EditPart, * int) */ protected void addChildVisual(EditPart childEditPart, int index) { boolean figureAdded = false; if (childEditPart instanceof NonVisualComponentEditPart) { getNonVisualElementBar().addNonVisualChild(((NonVisualComponentEditPart) childEditPart)); figureAdded = true; //TODO: need better flow of control. return; } Node childNode = (Node) childEditPart.getModel(); IFigure childFigure = ((GraphicalEditPart) childEditPart).getFigure(); ConvertPosition position = _tagConverter .getChildVisualPosition(childNode); if (position != null) { Node parent = position.getParentNode(); // link up figure. IFigure parentFigure = getFigure(parent); if (parentFigure != null) { parentFigure.add(childFigure, position.getIndex()); figureAdded = true; } // link up style if (parent instanceof INodeNotifier) { ICSSStyle parentStyle = (ICSSStyle) ((INodeNotifier) parent) .getAdapterFor(ICSSStyle.class); if (parentStyle != null) { ICSSStyle childStyle = (ICSSStyle) ((INodeNotifier) childNode) .getAdapterFor(ICSSStyle.class); if (childStyle instanceof AbstractStyle) { ((AbstractStyle) childStyle) .setParentStyle(parentStyle); } } } // link up the nodeForFigure if (childEditPart instanceof SubNodeEditPart) { Node nodeForFigure = ((SubNodeEditPart) childEditPart) .getNodeForFigure(); if (nodeForFigure != null /* * && !(nodeForFigure instanceof * PseudoElement) */) { parent.appendChild(nodeForFigure); } } } else { _log.error("getChildVisualPosition() return null"); //$NON-NLS-1$ } if (!figureAdded) { super.addChildVisual(childEditPart, index); } } /* * (non-Javadoc) * * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#removeChildVisual(org.eclipse.gef.EditPart) */ protected void removeChildVisual(EditPart childEditPart) { // remove figure IFigure childFigure = ((GraphicalEditPart) childEditPart).getFigure(); IFigure parent = childFigure.getParent(); if (parent != null) { parent.remove(childFigure); } if (childEditPart instanceof NonVisualComponentEditPart) { _nonVisualElementBar.removeNonVisualChild((NonVisualComponentEditPart) childEditPart); } // this only applies to visual edit parts else { // de-link style Node childNode = (Node) childEditPart.getModel(); ICSSStyle childStyle = (ICSSStyle) ((INodeNotifier) childNode) .getAdapterFor(ICSSStyle.class); if (childStyle instanceof AbstractStyle) { ((AbstractStyle) childStyle).setParentStyle(null); } // de-link nodeForFigure if (childEditPart instanceof SubNodeEditPart) { Node nodeForFigure = ((SubNodeEditPart) childEditPart) .getNodeForFigure(); if (nodeForFigure != null && nodeForFigure.getParentNode() != null) { nodeForFigure.getParentNode().removeChild(nodeForFigure); } } } } /** * @return the associated tag converter */ public ITagConverter getTagConvert() { return _tagConverter; } /* * (non-Javadoc) * * @see org.eclipse.wst.sse.core.internal.provisional.INodeAdapter#notifyChanged(org.eclipse.wst.sse.core.internal.provisional.INodeNotifier, * int, java.lang.Object, java.lang.Object, java.lang.Object, int) */ public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { // XXX: this can cause multiple refreshes on the same edit part for the // same change. I can also cause incorrect child refreshes... refresh(); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.parts.SubNodeEditPart#getNodeForFigure() */ public Node getNodeForFigure() { return _tagConverter.getResultElement(); } /** * @return true this part's node has non whitespace child nodes */ public boolean haveNonWhitespaceTextChild() { List children1 = this.getChildren(); for (int i = 0, size = children1.size(); i < size; i++) { if (children1.get(i) instanceof TextEditPart) { IDOMText xmltext = (IDOMText) ((TextEditPart) children1.get(i)) .getIDOMNode(); if (!xmltext.isElementContentWhitespace()) { return true; } } } return false; } private ElementMenuBar getNonVisualElementBar() { if (_nonVisualElementBar == null) { _nonVisualElementBar = new ElementMenuBar(this); } return _nonVisualElementBar; } /** * @return the element menu bar for this element */ public ElementMenuBar getElementMenuBar() { return getNonVisualElementBar(); } public void deactivate() { super.deactivate(); if (_nonVisualElementBar != null) { _nonVisualElementBar.dispose(); _nonVisualElementBar = null; } if (_tagConverter != null) { _tagConverter.dispose(); } } public Cursor getCursor(Point mouseLocation) { // let the selection edit policy dictate EditPolicy editPolicy = getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE); if (editPolicy instanceof IEnhancedSelectionEditPolicy) { return ((IEnhancedSelectionEditPolicy)editPolicy).getSelectionToolCursor(mouseLocation); } return super.getCursor(mouseLocation); } }