blob: 14771a8cac3daec0f29c2c37d2a9371fd8f5b45e [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2006 IBM Corporation 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:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.diagram.ui.editparts;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.diagram.core.listener.NotificationUtil;
import org.eclipse.gmf.runtime.notation.DrawerStyle;
import org.eclipse.gmf.runtime.notation.Filtering;
import org.eclipse.gmf.runtime.notation.FilteringStyle;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.Sorting;
import org.eclipse.gmf.runtime.notation.SortingDirection;
import org.eclipse.gmf.runtime.notation.SortingStyle;
import org.eclipse.gmf.runtime.notation.View;
/**
* A List compartment Edit part that contains semantic edit parts. A semantic
* edit part is an edit part that controls a semantic element, it does not
* control a Notation <code>View<code>.
* This list compartment is canonical by default, to turn off the canonical
* support just override the isCanonicalEnabled and return false
*
* To convert your List compartment Edit Part to a semantic List compartment, make the Edit Part
* extends <code>SemanticListCompartmentEditPart</code> then implement the abstrtact methods:
*
* semanticChildAdded(EObject child,int index)
* Which will be called when a semantic child is added
* And, getSemanticChildrenList()
* Which returns a list of all semantic children inside this editpart's model
*
* Then on the EditPart's you add to this list make sure thathasNotationView
* returns <code>false</code>
*
* @author mmostafa
*/
abstract public class SemanticListCompartmentEditPart
extends ListCompartmentEditPart {
/**
* constructor
* @param model the mdoel controlled by this edit part
*/
public SemanticListCompartmentEditPart(EObject model) {
super(model);
}
/* (non-Javadoc)
* @see org.eclipse.gef.editparts.AbstractEditPart#refreshChildren()
*/
protected void refreshChildren() {
int i;
GraphicalEditPart editPart;
List modelObjects = getModelChildren();
List _children = getChildren();
if ((modelObjects.size() == 0) && (_children.size() == 0)) {
return;
}
Map modelToEditPart = new HashMap();
for (i = 0; i < _children.size(); i++) {
editPart = (GraphicalEditPart)_children.get(i);
modelToEditPart.put(editPart.basicGetModel(), editPart);
}
for (i = 0; i < modelObjects.size(); i++) {
EObject element = (EObject)modelObjects.get(i);
//Do a quick check to see if editPart[i] == model[i]
if (i < _children.size()
&& (((GraphicalEditPart)_children.get(i)).basicGetModel()) == element)
continue;
//Look to see if the EditPart is already around but in the wrong location
editPart = (GraphicalEditPart)modelToEditPart.get(element);
if (editPart != null)
reorderChild (editPart, i);
else {
//An editpart for this model doesn't exist yet. Create and insert one.
semanticChildAdded(element,i);
}
}
List trash = new ArrayList();
for (; i < _children.size(); i++)
trash.add(_children.get(i));
for (i = 0; i < trash.size(); i++) {
EditPart ep = (EditPart)trash.get(i);
removeChild(ep);
}
}
/**
* @param child
*/
protected void semanticChildRemoved(EObject child){
if (children==null)
return;
for (Iterator iter = children.iterator(); iter.hasNext();) {
GraphicalEditPart ep = (GraphicalEditPart) iter.next();
if (ep.basicGetModel().equals(child)){
removeChild(ep);
break;
}
}
}
/**
* Updates the set of children views so that it
* is in sync with the semantic children. This method is called
* in response to notification from the model.
* <P>
* The update is performed by comparing the exising views with the set of
* semantic children returned from {@link #getViewChildren()}. Views whose
* semantic element no longer exists are {@link #deleteViews(Iterator) removed}.
* New semantic children have their View {@link #createViews(List)
* created}. Subclasses must override <code>getSemanticChildren()</code>.
* <P>
* Unlike <code>AbstractEditPart#refreshChildren()</code>, this refresh will not
* reorder the view list to ensure both it and the semantic children are
* in the same order since it is possible that this edit policy will handle
* a specifc subset of the host's views.
* <P>
* The host is refreshed if a view has created or deleted as a result of this
* refresh.
*/
protected void refreshSemanticChildren() {
if (!isCanonicalEnabled())
return ;
// Don't try to refresh children if the semantic element
// cannot be resolved.
if (resolveSemanticElement() == null) {
return;
}
//
// current EditParts
List editPartsChildren = getChildren();
List modelChildren = getModelChildren();
if ((editPartsChildren.size() == 0) && (modelChildren.size() == 0)) {
return;
}
List semanticChildren = new ArrayList(modelChildren);
//View viewChild;
EObject semanticChild;
Iterator childrenIT = editPartsChildren.iterator();
while( childrenIT.hasNext() ) {
GraphicalEditPart eP = (GraphicalEditPart)childrenIT.next();
semanticChild = (EObject)eP.basicGetModel();
semanticChildren.remove(semanticChild);
}
//
// create a view for each remaining semantic element.
if ( !semanticChildren.isEmpty()) {
for (Iterator iter = semanticChildren.iterator(); iter.hasNext();) {
EObject element = (EObject) iter.next();
semanticChildAdded(element,-1);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.gef.EditPart#activate()
*/
public void activate() {
super.activate();
refreshSemanticChildren();
}
/**
* This method will be called when a child is added to the
* EditPart's model id Canonical is enabled
* @param child the child being added, the index where its edit part should be created
* @param index
*/
abstract protected void semanticChildAdded(EObject child,int index);
/**
* Returns a list of all semantic children inside this editpart's model
* @return
*/
abstract protected List getSemanticChildrenList();
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart#handleNotificationEvent(org.eclipse.emf.common.notify.Notification)
*/
protected void handleNotificationEvent(Notification event) {
Object feature = event.getFeature();
if (hasModelChildrenChanged(event)){
semanticChildrenListChanged(event);
}
else if ((NotationPackage.eINSTANCE.getDrawerStyle_Collapsed() == feature ||
NotationPackage.eINSTANCE.getView_Visible() == feature&&
!event.getNewBooleanValue())||
(NotationPackage.eINSTANCE.getView_Visible() == feature &&
event.getNewBooleanValue())){
refreshSemanticChildren();
}
super.handleNotificationEvent(event);
}
/**
* called by the <code>handlePropertyChangeEvent</code> when the semantic children list
* is changed, then this method will check if the change was add or delete of an element
* and calls either <code>semanticChildAdded</code> or <code>semanticChildDeleted</code>
* this could be used to implement a canonical list without a canonical edit policy
* @param evt
*/
protected void semanticChildrenListChanged(Notification event) {
if (isCanonicalEnabled()){
if (NotificationUtil.isElementAddedToSlot(event) ||
NotificationUtil.isMove(event)){
refreshChildren();
} else if (NotificationUtil.isElementRemovedFromSlot(event)) {
if (event.getOldValue() instanceof Collection) {
for (Iterator iter = ((Collection) event
.getOldValue()).iterator(); iter.hasNext();) {
EObject removedObject = (EObject) iter.next();
semanticChildRemoved(removedObject);
}
} else if (event.getOldValue() instanceof EObject){
semanticChildRemoved((EObject)event.getOldValue());
}
}
} else if (NotificationUtil.isElementRemovedFromSlot(event))
if (!getChildren().isEmpty()) {
if (event.getOldValue() instanceof Collection) {
for (Iterator iter = ((Collection) event
.getOldValue()).iterator(); iter.hasNext();) {
EObject removedObject = (EObject) iter.next();
semanticChildRemoved(removedObject);
}
} else if (event.getOldValue() instanceof EObject) {
semanticChildRemoved((EObject)event.getOldValue());
}
}
}
/**
* indicated if canonical is enabled or not Canonical is disabled if the
* edit part's view is collapsed or hidden
*
* @return
*/
protected boolean isCanonicalEnabled() {
DrawerStyle dstyle = (DrawerStyle) ((View) getModel())
.getStyle(NotationPackage.Literals.DRAWER_STYLE);
boolean isCollapsed = dstyle == null ? false
: dstyle.isCollapsed();
if (isCollapsed) {
return false;
}
return ((View) getModel()).isVisible();
}
/**
* indicated if canonical is enabled or not Canonical is disabled if the
* edit part's view is collapsed or hidden
*
* @return
*/
public boolean isCanonicalOn() {
return isCanonicalEnabled();
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart#getSortedChildren()
*/
protected List getSortedChildren() {
SortingStyle style = (SortingStyle) ((View)getModel()).getStyle(NotationPackage.eINSTANCE.getSortingStyle());
if (style != null) {
Sorting sorting = style.getSorting();
if (Sorting.NONE_LITERAL == sorting) {
return getSemanticChildrenList();
} else if (Sorting.MANUAL_LITERAL == sorting) {
List allChildren = getSemanticChildrenList();
// Return the empty list if the model children have not yet been
// created.
if (allChildren.isEmpty())
return allChildren;
List sortedChildren = style.eIsSet(NotationPackage.eINSTANCE.getSortingStyle_SortedObjects())
? style.getSortedObjects()
: Collections.EMPTY_LIST;
List sorted = new ArrayList();
// Get the corresponding views...
Iterator i = sortedChildren.iterator();
while(i.hasNext()) {
EObject eObject = (EObject) i.next();
if (allChildren.contains(eObject))
sorted.add(eObject);
}
// Add any remaining model children to the end
for (int j = 0; j < allChildren.size(); j++) {
EObject element = (EObject) allChildren.get(j);
if (!sorted.contains(element))
sorted.add(element);
}
return sorted;
} else if (Sorting.AUTOMATIC_LITERAL == sorting) {
Map sortingKeys = style.eIsSet(NotationPackage.eINSTANCE.getSortingStyle_SortingKeys())
? style.getSortingKeys()
: Collections.EMPTY_MAP;
return getChildrenSortedBy(sortingKeys);
}
}
return getSemanticChildrenList();
}
/**
* Returns the model children sorted by the order specified by Properties.ID_SORTING_KEYS.
* This is used to support dynamic list compartment sorting.
* @param sortingKeys
* @return the model children sorted
*/
protected List getChildrenSortedBy(Map sortingKeys) {
List allChildren = new ArrayList(getSemanticChildrenList());
// Currently only one sorting key is supported.
if (sortingKeys != null && !sortingKeys.isEmpty()) {
Collection keySet = sortingKeys.keySet();
Iterator i = keySet.iterator();
String name = (String) i.next();
SortingDirection direction = (SortingDirection) sortingKeys.get(name);
Comparator comparator = getComparator(name, direction);
if (comparator != null)
Collections.sort(allChildren, getComparator(name, direction));
}
return allChildren;
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart#getFilteredChildren()
*/
protected List getFilteredChildren() {
Object model = getModel();
if (model instanceof View){
View view = (View)model;
FilteringStyle style = (FilteringStyle) view.getStyle(NotationPackage.eINSTANCE.getFilteringStyle());
if (style != null) {
Filtering filtering = style.getFiltering();
if (Filtering.NONE_LITERAL == filtering) {
return Collections.EMPTY_LIST;
} else if (Filtering.MANUAL_LITERAL == filtering) {
List filteredChildren = style.eIsSet(NotationPackage.eINSTANCE.getFilteringStyle_FilteredObjects())
? style.getFilteredObjects()
: Collections.EMPTY_LIST;
return filteredChildren;
} else if (Filtering.AUTOMATIC_LITERAL == filtering) {
List filteringKeys = style.eIsSet(NotationPackage.eINSTANCE.getFilteringStyle_FilteringKeys())
? style.getFilteringKeys()
: Collections.EMPTY_LIST;
return getChildrenFilteredBy(filteringKeys);
}
}
}
return Collections.EMPTY_LIST;
}
public boolean isCanonical() {
return true;
}
}