blob: f85bfbaabcfef29c620c35eb3bfb2158d0080ec8 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}