blob: a735178a8688bba35603f0909220b9b4429fdff1 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2005 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.common.ui.dialogs;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
/**
* An element that contains a hint, a serializable String ID, a String label,
* and an ImageDescriptor label. SeletableElements keep track of its parent
* which may or may not be null and a list of children which may be empty.
* This allows SelectableElement objects to be optionally represented in tree
* viewers. SelectableElements also keep track of their SelectedType, which
* describes if the element is selected, unselected, or set to leave.
* Therefore, SelectableElement objects typically correspond to elements in the
* UI.
*
* <P>This class contains public convenience methods. For eaxmple, it
* includes methods to find SelectableElement objects or their hints based on
* their String ID and a method to make copies of SelectableElement objects.
*
* <P>The SelectableElement class is used in at least 3 places, Show Related
* Elements Show Hide Relationships, and Browse Diagrams.
*
* @author Wayne Diu, wdiu
*/
public class SelectableElement {
/**
* Unique identifier for this selectable element.
*/
private String id;
/**
* String name of the element
*/
private String name;
/**
* Icon for the element
*/
private ImageDescriptor icon;
/**
* This element's children
*/
private Vector children;
/**
* True if element was checked by the user, false if it wasn't
*/
private SelectedType selectedType;
/**
* This element's parent
*/
private SelectableElement parent;
/**
* Hint for checking what the type of this element is
*/
private Object hint;
/**
* Adds a child to this element
*
* @param element
* the child to add
*/
public void addChild(SelectableElement element) {
children.add(element);
element.setParent(this);
}
/**
* Remove all children of this SelectableElement
*/
public void removeAllChildren() {
Iterator i = children.iterator();
while (i.hasNext()) {
((SelectableElement) i.next()).setParent(null);
}
children = new Vector();
}
/**
* Constructor to make a new SelectableElement
*
* @param aName
* the String name of the element
* @param anIcon
* the icon Image for the element
* @param aHint
* the element type
* @deprecated Use the other constructor.
*/
public SelectableElement(String aName, ImageDescriptor anIcon, Object aHint) {
// For now, we will use the name as a unique identifier.
this(aName, aName, anIcon, aHint);
}
/**
* Constructor to make a new SelectableElement.
*
* @param anID
* A non-language specific unique identifier for this selectable
* element.
* @param aName
* A user-presentable name for this selectable element.
* @param anIcon
* The icon image for the element.
* @param aHint
* A hint associated with the selection of this element.
*/
public SelectableElement(String anID, String aName, ImageDescriptor anIcon,
Object aHint) {
children = new Vector();
this.id = anID;
this.name = aName;
this.icon = anIcon;
this.hint = aHint;
selectedType = SelectedType.UNSELECTED;
}
/**
* Returns the number of children
*
* @return int with the number of children this element has
*/
public int getNumberOfChildren() {
return children.size();
}
/**
* Returns a child
*
* @param i
* with the index of the child of this element
* @return SelectableElement which is child i of this element
*/
public SelectableElement getChild(int i) {
assert (i >= 0 && i < children.size());
return (SelectableElement) children.get(i);
}
/**
* Returns the icon of this element to display to the user.
*
* @return Image with icon of this element to display to the user
*/
public ImageDescriptor getIconImageDescriptor() {
return icon;
}
/**
* Returns the name of this element to display to the user.
*
* @return String with name of this element to display to the user
*/
public String getName() {
return name;
}
/**
* Sets the icon of this element to display to the user.
*
* @param anIcon
* The icon to set
*/
public void setIconImageDescriptor(ImageDescriptor anIcon) {
this.icon = anIcon;
}
/**
* Sets the name of this element to display to the user.
*
* @param aName
* The name to set
*/
public void setName(String aName) {
this.name = aName;
}
/**
* Returns the parent of this element.
*
* @return SelectableElement
*/
public SelectableElement getParent() {
return parent;
}
/**
* Sets the parent of this element.
*
* @param aParent
* The parent to set
*/
public void setParent(SelectableElement aParent) {
this.parent = aParent;
}
/**
* Returns the children of this element as an array
*
* @return SelectableElement[] array of this element's children.
*/
public SelectableElement[] getChildren() {
SelectableElement elements[] = new SelectableElement[getNumberOfChildren()];
for (int i = 0; i < getNumberOfChildren(); i++) {
elements[i] = (SelectableElement) children.elementAt(i);
}
return elements;
}
/**
* Returns if the element was selected.
*
* @return selectedType from the SelectedType EnumeratedType
*/
public SelectedType getSelectedType() {
return selectedType;
}
/**
* Sets whether or not the element is selected. For example, if the element
* is checked in the interface.
*
* @param aSelectedType
* from the SelectedType EnumeratedType
*/
public void setSelectedType(SelectedType aSelectedType) {
this.selectedType = aSelectedType;
}
/**
* Returns the hint, which is an Object. This could be subclassed if type
* safety is required.
*
* @return the element type
*/
public Object getHint() {
return hint;
}
/**
* Sets the SelectedType for a SelectableElement and its children
*
* @param parent
* sets the SelectedType for this SelectableElement
* @param selectedType
* the SelectedType to set for the SelectableElement and its
* children.
*/
public static void setSelectedTypeForSelecteableElementAndChildren(
SelectableElement parent, SelectedType selectedType) {
for (int i = 0; i < parent.getNumberOfChildren(); i++) {
setSelectedTypeForSelecteableElementAndChildren(parent.getChild(i),
selectedType);
}
parent.setSelectedType(selectedType);
}
/**
* Sets the SelectedType for a SelectableElement and its children that match
* the IDs in the List of IDs.
*
* @param parent
* sets the SelectedType for this SelectableElement
* @param selectedType
* the SelectedType to set for the SelectableElement and its
* children.
* @param list
* List of IDs, not hints
*/
public static void setSelectedTypeForMatchingSelecteableElementAndChildren(
SelectableElement parent, SelectedType selectedType, List list) {
for (int i = 0; i < parent.getNumberOfChildren(); i++) {
setSelectedTypeForMatchingSelecteableElementAndChildren(parent
.getChild(i), selectedType, list);
}
if (list.contains(parent.getId())) {
setSelectedTypeForSelecteableElementAndChildren(parent,
selectedType);
}
}
/**
* Calculates the longest string length of this element's children for the
* text that will be displayed in the control. This method only works for
* root SelectableElements.
*
* @param selectableElement
* the SelectableElement to calculate the longest string length.
* Also looks at its children.
* @param control
* the control with the font to use when calculating the font
* size
* @return int with the string length in pixels
*/
public static int calculateLongestStringLength(
SelectableElement selectableElement, Control control) {
int INITIAL_LONGEST_STRING_LENGTH = 0;
int INITIAL_ITERATION_LEVEL = -1; //there is a fake element
assert (selectableElement.getParent() == null);
GC gc = new GC(control);
int longestStringLength = calculateLongestStringLength(
selectableElement, gc, INITIAL_LONGEST_STRING_LENGTH,
INITIAL_ITERATION_LEVEL);
gc.dispose();
return longestStringLength;
}
/**
* Calculates the longest string length of this element's children for the
* text that will be displayed.
*
* @param selectableElement
* the SelectableElement to calculate the longest string length.
* Also looks at its children.
* @param gc
* the GC to use when calculating the font size
* @param longestStringLength
* the longest string length for the selectableElement and its
* children so far.
* @param iterationLevel
* how many levels we have gone to keep track of the indents of
* the icons
* @return int with the string length in pixels
*/
private static int calculateLongestStringLength(
SelectableElement selectableElement, GC gc,
int longestStringLength, int iterationLevel) {
int ICON_WIDTH = 32;
//we don'internationalize these icons, checkbox is 16 and image is 16
Point size = gc.textExtent(selectableElement.getName());
if (size.x + (iterationLevel * ICON_WIDTH) > longestStringLength)
longestStringLength = size.x + (iterationLevel * ICON_WIDTH);
for (int i = 0; i < selectableElement.getNumberOfChildren(); i++) {
longestStringLength = calculateLongestStringLength(
selectableElement.getChild(i), gc, longestStringLength,
iterationLevel + 1);
}
return longestStringLength;
}
/**
* Returns the number of children including itself. Includes children that
* are children of children, etc.
*
* @param selectableElement
* the SelectableElement that we will find the number of children
* for.
* @return int the number of children including children that are children
* of children, etc, and itself.
*/
public static int calculateNumberOfChildren(
SelectableElement selectableElement) {
int numberOfChildren = 0;
for (int i = 0; i < selectableElement.getNumberOfChildren(); i++) {
numberOfChildren += calculateNumberOfChildren(selectableElement
.getChild(i));
}
return numberOfChildren + 1;
}
/**
* Recursively checks SelectableElement and its children to determine which
* elements have the specified selectedType. If a SelectableElement meets
* the type criteria, its hint (RelationshipType) is added to the
* matchingElements list.
*
* @param matchingElements
* @param typeToMatch
*/
private void getMatchingElementTypes(List matchingElements,
SelectedType typeToMatch) {
for (int i = 0; i < getNumberOfChildren(); i++) {
getChild(i).getMatchingElementTypes(matchingElements, typeToMatch);
}
if (getSelectedType() == typeToMatch && getHint() != null) {
if (getHint() instanceof Collection)
matchingElements.addAll((Collection) getHint());
else
matchingElements.add(getHint());
}
}
/**
* Returns a list of SELECTED RelationshipTypes for a SelectableElement.
*
* Checks this SelectableElement and the SelectableElement's children. For
* each SelectableElement where the selectedType is SELECTED, add the
* RelationshipType to a list.
*
* @return List
*/
public List getSelectedElementTypes() {
List selectedElements = new Vector();
getMatchingElementTypes(selectedElements, SelectedType.SELECTED);
return selectedElements;
}
/**
* Returns a list of UNSELECTED RelationshipTypes for a SelectableElement.
*
* Checks this SelectableElement and the SelectableElement's children. For
* each SelectableElement where the selectedType is UNSELECTED, add the
* RelationshipType to a list.
*
* @return List
*/
public List getUnSelectedElementTypes() {
List unselectedElements = new Vector();
getMatchingElementTypes(unselectedElements, SelectedType.UNSELECTED);
return unselectedElements;
}
/**
* Returns a list of LEAVE RelationshipTypes for a SelectableElement.
*
* Checks this SelectableElement and the SelectableElement's children. For
* each SelectableElement where the selectedType is LEAVE, add the
* RelationshipType to a list.
*
* @return List
*/
public List getLeaveElementTypes() {
List leaveElements = new Vector();
getMatchingElementTypes(leaveElements, SelectedType.LEAVE);
return leaveElements;
}
/**
* Returns a string representation of this selectable element. This is
* useful if a selectable element must be persisted between invocations of
* eclipse.
*
* @return String id of this selectableElement
*/
public String getId() {
return id;
}
/**
* Retrieves all of the hints from the list of selections. The selections
* are strings that have been produced by the getId() method. Note: this
* should be invoked from the selectable element root.
*
* @param stringRepresentations
* Strings produced by the {@link SelectableElement#getId()}
* method.
* @param hints
* (out) A set used to store all of the hints.
*/
public void getHints(List stringRepresentations, Set hints) {
// If we have hit a string representation that is fully selected
// then we simply retrieve all of the hints for this subtree. For
// this selectable element to have been in the "SELECTED" state, it
// and all of its children were selected.
if (stringRepresentations.contains(getId())) {
getAllHints(hints);
} else {
for (Iterator i = children.iterator(); i.hasNext();) {
((SelectableElement) i.next()).getHints(stringRepresentations,
hints);
}
}
}
/**
* Retrieve all hints for the subtree rooted at this selectable element.
*
* @param hints
* (out) A set used to store all of the hints.
*/
public void getAllHints(Set hints) {
if (hint instanceof List)
hints.addAll((List) hint);
else if (hint != null)
hints.add(hint);
for (Iterator i = children.iterator(); i.hasNext();) {
((SelectableElement) i.next()).getAllHints(hints);
}
}
/**
* Recursively add this SelectableElement's and this SelectableElement's
* children's hints to a List which is not null.
*
* It will not add duplicates into the List, and if the hint is null, it
* will not be added to the List.
*
* @param list
* not null, add hints to this List
* @param selectableElement
* recursively add hints from this SelectableElement and its
* children
*/
public static void addHintsToList(List list,
SelectableElement selectableElement) {
assert null != list;
assert null != selectableElement;
for (int i = 0; i < selectableElement.getNumberOfChildren(); i++) {
addHintsToList(list, selectableElement.getChild(i));
}
Object hint = selectableElement.getHint();
if (hint != null) {
//I disagree that hint should be sometimes a List, and sometimes an
//element, but I will support it
if (hint instanceof List) {
Iterator it = ((List) hint).iterator();
while (it.hasNext()) {
Object nestedHint = it.next();
if (!list.contains(nestedHint)) {
list.add(nestedHint);
}
}
} else if (!list.contains(hint))
list.add(hint);
}
}
/**
* Returns if all children have the same selected type
*
* @return true if all children are checked, false otherwise
* @param parent
* we'll be checking the children of this parent
* @param selectType
* the SelectedType that all children of the parent are checked
* for
*/
public static boolean doAllChildrenHaveSelectedType(
SelectableElement parent, SelectedType selectType) {
for (int i = 0; i < parent.getNumberOfChildren(); i++) {
if (parent.getChild(i).getSelectedType() != selectType)
return false;
}
return true;
}
/**
* Return all children that have the SelectedType.
*
* @param parent
* parent selectable element.
* @param selectType
* the selected type to match
* @param list
* of SelectableElements
*/
public static void getAllChildrenOfType(SelectableElement parent,
SelectedType selectType, List list) {
assert null != list;
for (int i = 0; i < parent.getNumberOfChildren(); i++) {
if (parent.getChild(i).getSelectedType() == selectType) {
list.add(parent.getChild(i));
}
getAllChildrenOfType(parent.getChild(i), selectType, list);
}
}
/**
* Return element IDs, including children, that are SelectedType.SELECTED.
*
* @return List of element IDs, including children, that are
* SelectedType.SELECTED.
*/
public List getSelectedElementIds() {
List list = new ArrayList();
getMatchingElementIds(list, SelectedType.SELECTED);
return list;
}
/**
* Return matching element IDs that match the typeToMatch.
*
* @param matchingIds
* List of String ids we are filling
* @param typeToMatch
* going to match this type
*/
private void getMatchingElementIds(List matchingIds,
SelectedType typeToMatch) {
for (int i = 0; i < getNumberOfChildren(); i++) {
getChild(i).getMatchingElementIds(matchingIds, typeToMatch);
}
if (getSelectedType() == typeToMatch) {
matchingIds.add(getId());
}
}
/**
* Same idea as the clone method. Share the same images, hints, etc. of the
* original. Just have different SelectableElements.
*
* @return a copy of this selectableElement
*/
public SelectableElement makeCopy() {
SelectableElement selectableElement = immediateCopy(this);
copyChildren(this, selectableElement);
return selectableElement;
}
/**
* Used by the copy method. Not suprisingly, this returns a copy of the src
*
* @param src
* children will be copied from here
* @return SelectableElement which is a copy of the src
*/
private SelectableElement immediateCopy(SelectableElement src) {
SelectableElement selectableElement = new SelectableElement(
src.getId(), src.getName(), src.getIconImageDescriptor(), src
.getHint());
selectableElement.setSelectedType(src.getSelectedType());
return selectableElement;
}
/**
* Used by the copy method. Not surprisingly, this copies the children of
* src into dest.
*
* @param src
* children will be copied from here
* @param dest
* children are copied into here
*/
private void copyChildren(SelectableElement src, SelectableElement dest) {
for (int i = 0; i < src.getNumberOfChildren(); i++) {
dest.addChild(immediateCopy(src.getChild(i)));
copyChildren(src.getChild(i), dest.getChild(i));
}
}
/**
* Collect all the hints of the children into a list.
*
* @param list
* that I am collecting the hints into.
*/
private void collectChildrenHints(List list) {
Object aHint = getHint();
if (aHint instanceof List) {
Iterator it = ((List) aHint).iterator();
while (it.hasNext()) {
Object obj = it.next();
if (!list.contains(obj))
list.add(obj);
}
} else if (aHint != null) {
if (!list.contains(aHint))
list.add(aHint);
}
for (int i = 0; i < getChildren().length; i++) {
getChild(i).collectChildrenHints(list);
}
}
/**
* Collect the types that match a list of String ids. Unlike getHints, which
* doesn't collect children hints.
*
* @param list
* List to add into
* @param ids
* List of String ids we are trying to match
*/
public void getHintsThatMatchTheseIds(List list, List ids) {
for (int i = 0; i < getNumberOfChildren(); i++) {
if (ids.contains(getChild(i).getId())) {
getChild(i).collectChildrenHints(list);
} else {
getChild(i).getHintsThatMatchTheseIds(list, ids);
}
}
}
/**
* Return the first element that matches the given id from the List of
* SelectableElement objects
*
* @param selectableElements
* List of SelectableElement objects to match
* @param id
* String id to match
* @return the first element that matches the given id from the List of
* SelectableElement objects or null if nothing matched
*/
public static SelectableElement findById(List selectableElements, String id) {
assert null != selectableElements;
Iterator it = selectableElements.iterator();
while (it.hasNext()) {
Object obj = it.next();
assert (obj instanceof SelectableElement);
SelectableElement selectableElement = (SelectableElement) obj;
if (id.equals(selectableElement.getId())) {
return selectableElement;
}
}
return null;
}
/**
* Return the first element that matches the given id from this
* SelectableElement and its children
*
* @param theId
* String id to match
* @return the first element that matches the given id from this
* SelectableElement and its children or null if nothing matched
*/
public SelectableElement findById(String theId) {
assert null != theId;
if (theId.equals(this.getId())) {
return this;
}
for (int i = 0; i < getNumberOfChildren(); i++) {
SelectableElement element = getChild(i).findById(theId);
if (element != null)
return element;
}
return null;
}
}