Files
Brainstorm/Foundation SWT/src/com/foundation/view/swt/DynamicMenu.java

1195 lines
48 KiB
Java
Raw Normal View History

2014-05-30 10:31:51 -07:00
/*
* Copyright (c) 2008,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.view.swt;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import com.common.comparison.Comparator;
import com.common.util.ICollection;
import com.common.util.IIterator;
import com.common.util.IList;
import com.common.util.LiteHashMap;
import com.common.util.LiteList;
import com.foundation.util.IInlineCollectionObservable;
import com.foundation.util.IInlineCollectionObserver;
import com.foundation.util.IInlineIndexedCollectionObservable;
import com.foundation.util.IInlineIndexedCollectionObserver;
import com.foundation.view.IAbstractContainer;
import com.foundation.view.IMethodAssociation;
import com.foundation.view.IViewContext;
import com.foundation.view.JefImage;
import com.foundation.view.MultiAssociationContainer;
import com.foundation.view.MultiResourceAssociation;
import com.foundation.view.SingleAssociationContainer;
import com.foundation.view.SingleResourceAssociation;
import com.foundation.view.resource.AbstractResourceService;
public class DynamicMenu extends AbstractComponent implements IAbstractMenu {
/** The menu's parent menu component. */
private Menu parent = null;
/** The dynamic menu's name. */
private String name = null;
/** The collection of DynamicMenuDefinition instances in order created (and order to be searched). */
private LiteList definitions = new LiteList(10, 20);
/** The association providing a collection of objects that can be translated into menus via the menu definitions. */
private SingleResourceAssociation collection = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_OBJECT, false, null);
/** The root collection container, containing the collection of values that make up the root level menus. */
private CollectionContainer rootContainer = null;
/** The map of menu data indexed by the object that it describes. */
private LiteHashMap menuDataMap = new LiteHashMap(100, Comparator.getIdentityComparator(), Comparator.getIdentityComparator());
/**
* Captures and directs item collection changes.
*/
private class CollectionContainer implements IInlineCollectionObserver, IInlineIndexedCollectionObserver {
/** The collection the metadata describes and manages. */
private ICollection collection = null;
/** The optional parent for the menus created by the collection's values. */
private CascadeMenuData parent = null;
/**
* CollectionContainer constructor.
* @param collection The collection being managed.
* @param parent The parent menu for the menu's created by the collection's values.
*/
private CollectionContainer(ICollection collection, CascadeMenuData parent) {
this.collection = collection;
this.parent = parent;
}//CollectionContainer()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineIndexedCollectionObserver#valueAdded(java.lang.Object, int)
*/
public void valueAdded(Object value, int index) {
if(menuDataMap.containsKey(value)) {
throw new RuntimeException("Recursive menus are not supported by dynamic menu.");
}//if//
for(int definitionIndex = 0; value != null && definitionIndex < definitions.getSize(); definitionIndex++) {
DynamicMenuDefinition definition = (DynamicMenuDefinition) definitions.get(definitionIndex);
if(definition.matches(value)) {
definition.add(value, parent);
value = null;
}//if//
}//for//
}//valueAdded()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#valueAdded(java.lang.Object)
*/
public void valueAdded(Object value) {
valueAdded(value, -1);
}//valueAdded()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineIndexedCollectionObserver#valueRemoved(java.lang.Object, int)
*/
public void valueRemoved(Object value, int index) {
for(int definitionIndex = 0; value != null && definitionIndex < definitions.getSize(); definitionIndex++) {
DynamicMenuDefinition definition = (DynamicMenuDefinition) definitions.get(definitionIndex);
if(definition.matches(value)) {
definition.remove(value);
value = null;
}//if//
}//for//
}//valueRemoved()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#valueRemoved(java.lang.Object)
*/
public void valueRemoved(Object value) {
valueRemoved(value, -1);
}//valueRemoved()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineIndexedCollectionObserver#allRemoved()
*/
public void removingAll() {
//TODO: Ensure that this is as efficient as possible with regards to cleaning up children.
for(IIterator valueIterator = collection.iterator(); valueIterator.hasNext();) {
Object value = valueIterator.next();
for(int definitionIndex = 0; value != null && definitionIndex < definitions.getSize(); definitionIndex++) {
DynamicMenuDefinition definition = (DynamicMenuDefinition) definitions.get(definitionIndex);
if(definition.matches(value)) {
definition.remove(value);
value = null;
}//if//
}//for//
}//for//
}//removingAll()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineIndexedCollectionObserver#valuesSorted(int[])
*/
public void valuesSorted(int[] mapping) {
//TODO: Re-create the swt menus in this collection, and every collection that is a child.
}//valuesSorted()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#startChanges(int)
*/
public void startChanges(int changeCount) {
}//startChanges()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#stopChanges()
*/
public void stopChanges() {
}//stopChanges()//
/**
* Initializes the container by registering the collection hooks that allow the component to receive collection changes.
*/
public void initialize() {
if(collection instanceof IInlineIndexedCollectionObservable) {
((IInlineIndexedCollectionObservable) collection).addCollectionObserver((IInlineIndexedCollectionObserver) this);
}//if//
else if(collection instanceof IInlineCollectionObservable) {
((IInlineCollectionObservable) collection).addCollectionObserver((IInlineCollectionObserver) this);
}//else if//
}//registerCollectionHooks()//
/**
* Releases the container and cleans up all resources.
*/
public void release() {
//TODO: Is this efficient?
rootContainer.removingAll();
if(collection instanceof IInlineIndexedCollectionObservable) {
((IInlineIndexedCollectionObservable) collection).removeCollectionObserver((IInlineIndexedCollectionObserver) this);
}//if//
else if(collection instanceof IInlineCollectionObservable) {
((IInlineCollectionObservable) collection).removeCollectionObserver((IInlineCollectionObserver) this);
}//else if//
}//unregisterCollectionHooks()//
}//CollectionContainer//
/**
* The base class for the menu definitions. A definition allows a value to be converted to a menu.
* <p>TODO: Allow custom matching by allowing the view programmer to provide a method reference that takes an object and returns a boolean.</p>
*/
public abstract class DynamicMenuDefinition extends AbstractComponent {
/** The type of object the definition can handle. Any object that is, extends, or implements the type will be accepted by the definition. */
private Class type = null;
/** The menu's title resource. */
private MultiResourceAssociation text = new MultiResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_TEXT, false, "");
/** The menu's image resource. */
private MultiResourceAssociation image = new MultiResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_IMAGE, false, null);
/** The resource defining the visible state for the menu. */
private MultiResourceAssociation isVisible = new MultiResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_BOOLEAN, false, Boolean.TRUE);
/** The resource defining the enabled state for the menu. */
private MultiResourceAssociation isEnabled = new MultiResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_BOOLEAN, false, Boolean.TRUE);
/** A holder for the value of the text. */
protected MultiResourceHolder textHolder = new MultiResourceHolder(this);
/** A holder for the value of the image. */
protected MultiResourceHolder imageHolder = new MultiResourceHolder(this);
/** A holder for the value of the visibility flag. */
protected MultiResourceHolder isVisibleHolder = new MultiResourceHolder(this);
/** A holder for the value of the enabled state flag. */
protected MultiResourceHolder isEnabledHolder = new MultiResourceHolder(this);
/**
* Creates the menu data specific to this definition type.
* @return A new menu data.
*/
public abstract DynamicMenuData createMenuData();
/**
* Initializes the new menu data.
* @param dynamicMenuData The menu data to be initialized.
*/
public abstract void initializeMenuData(DynamicMenuData dynamicMenuData);
/**
* Releases the menu data.
* @param dynamicMenuData The menu data for the value no longer matched to a menu.
*/
public abstract void releaseMenuData(DynamicMenuData dynamicMenuData);
/**
* Creates the menu(s).
* @param dynamicMenuData The menu data whose system resources are to be created.
*/
public void createMenu(DynamicMenuData dynamicMenuData) {
dynamicMenuData.swtMenuItem.setText(dynamicMenuData.textValue);
dynamicMenuData.swtMenuItem.setImage(createImage(dynamicMenuData.imageValue));
dynamicMenuData.swtMenuItem.setEnabled(dynamicMenuData.isEnabledValue);
}//createMenu()//
/**
* Releases the resource(s) for the menu.
* @param dynamicMenuData The menu data whose system resources are to be released.
*/
public abstract void releaseMenu(DynamicMenuData dynamicMenuData);
/**
* Adds a value by creating the menu for the value within the given parent.
* @param value The value whose menu is to be created.
* @param parent The parent menu for the new menu, or null if the menu is a root menu.
*/
public void add(Object value, CascadeMenuData parent) {
DynamicMenuData menuData = createMenuData();
menuData.definition = this;
menuData.value = value;
menuData.parent = parent;
menuDataMap.put(value, menuData);
initializeMenuData(menuData);
internalViewInitialize(value);
internalViewRefresh(value);
}//add()//
/**
* Removes a value's menu from the given parent.
* @param value The value whose menu is to be created.
*/
public void remove(Object value) {
DynamicMenuData menuData = (DynamicMenuData) menuDataMap.get(value);
internalViewRelease(value);
releaseMenu(menuData);
releaseMenuData(menuData);
}//remove()//
/**
* Gets the menu data for the given value.
* @param value The value whose menu data is to be retreived.
* @return The menu data for the value, or null if no menu was ever created for the value.
*/
public DynamicMenuData getMenuData(Object value) {
return (DynamicMenuData) menuDataMap.get(value);
}//getMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewInitialize()
*/
protected void internalViewInitialize() {
image.initialize();
isEnabled.initialize();
isVisible.initialize();
text.initialize();
super.internalViewInitialize();
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewRefresh()
*/
protected void internalViewRefresh() {
super.internalViewRefresh();
}//internalViewRefresh()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewRelease()
*/
protected void internalViewRelease() {
image.release();
isEnabled.release();
isVisible.release();
text.release();
imageHolder.release();
textHolder.release();
isEnabledHolder.release();
isVisibleHolder.release();
super.internalViewRelease();
}//internalViewRelease()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewSynchronize()
*/
protected void internalViewSynchronize() {
super.internalViewSynchronize();
}//internalViewSynchronize()//
/**
* @param value
*/
protected void internalViewInitialize(Object value) {
image.registerItem(value);
isEnabled.registerItem(value);
isVisible.registerItem(value);
text.registerItem(value);
}//internalViewInitialize()//
/**
* @param alteredItem
*/
protected void internalViewRefresh(Object alteredItem) {
internalViewRefreshText(alteredItem);
internalViewRefreshImage(alteredItem);
internalViewRefreshIsEnabled(alteredItem);
internalViewRefreshIsVisible(alteredItem);
}//internalViewRefresh()//
/**
* @param value
*/
protected void internalViewRelease(Object value) {
image.unregisterItem(value);
isEnabled.unregisterItem(value);
isVisible.unregisterItem(value);
text.unregisterItem(value);
}//internalViewInitialize()//
/**
* Determines whether the value's class is compatable with the one the definition supports.
* @param value The value that will be checked against the type the definition supports.
* @return Whether the value is a type.
*/
public boolean matches(Object value) {
return value != null && type.isAssignableFrom(value.getClass());
}//matches()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalOnValueChanged(com.foundation.view.MultiResourceAssociation, java.lang.Object, boolean)
*/
protected void internalOnValueChanged(MultiResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isUpdate) {
if(resourceAssociation == isVisible) {
internalViewRefreshIsVisible(alteredItem);
}//if//
else if(resourceAssociation == isEnabled) {
internalViewRefreshIsEnabled(alteredItem);
}//else if//
else if(resourceAssociation == text) {
internalViewRefreshText(alteredItem);
}//else if//
else if(resourceAssociation == image) {
internalViewRefreshImage(alteredItem);
}//else if//
else {
super.internalOnValueChanged(resourceAssociation, alteredItem, data, isUpdate);
}//else//
}//internalOnValueChanged()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalResourceHolderChanged(com.foundation.view.swt.MultiResourceHolder, java.lang.Object, java.lang.Object, java.lang.Object)
*/
protected void internalResourceHolderChanged(MultiResourceHolder resource, Object row, Object oldValue, Object newValue) {
if(resource == textHolder) {
DynamicMenuData menuData = getMenuData(row);
menuData.textValue = newValue != null ? (String) newValue : "";
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.setText(menuData.textValue);
}//if//
}//if//
else if(resource == imageHolder) {
DynamicMenuData menuData = getMenuData(row);
menuData.imageValue = (JefImage) newValue;
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
destroyImage(menuData.swtMenuItem.getImage());
menuData.swtMenuItem.setImage(createImage(menuData.imageValue));
}//if//
}//else if//
else if(resource == isVisibleHolder) {
DynamicMenuData menuData = getMenuData(row);
menuData.isVisibleValue = ((Boolean) newValue).booleanValue();
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
// menuData.swtMenuItem.setVisible(menuData.isVisibleValue);
}//if//
}//else if//
else if(resource == isEnabledHolder) {
DynamicMenuData menuData = getMenuData(row);
menuData.isEnabledValue = ((Boolean) newValue).booleanValue();
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.setEnabled(menuData.isEnabledValue);
}//if//
}//else if//
else {
super.internalResourceHolderChanged(resource, row, oldValue, newValue);
}//else//
}//internalOnAssociationChanged()//
/**
* Refreshes the text from the text attribute and the default text.
*/
protected void internalViewRefreshText(Object alteredItem) {
if(text.refresh(alteredItem)) {
DynamicMenuData menuData = getMenuData(alteredItem);
if(menuData != null) {
Object value = text.getValue(alteredItem);
menuData.textValue = value == null ? "" : value.toString();
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.setText(menuData.textValue);
}//if//
}//if//
}//if//
}//internalViewRefreshText()//
/**
* Refreshes the image from the image url attribute and the default image url.
*/
protected void internalViewRefreshImage(Object alteredItem) {
if(image.refresh(alteredItem)) {
imageHolder.setValue(alteredItem, image.getValue(alteredItem));
}//if//
}//internalViewRefreshImage()//
/**
* Refreshes whether the menu is enabled based on the attribute value or if null the default flag value.
*/
protected void internalViewRefreshIsEnabled(Object alteredItem) {
if(isEnabled.refresh(alteredItem)) {
isEnabledHolder.setValue(alteredItem, isEnabled.getValue(alteredItem));
}//if//
}//internalViewRefreshIsEnabled()//
/**
* Refreshes whether the menu is visible based on the attribute value or if null the default flag value.
*/
protected void internalViewRefreshIsVisible(Object alteredItem) {
if(isVisible.refresh(alteredItem)) {
isVisibleHolder.setValue(alteredItem, isVisible.getValue(alteredItem));
}//if//
}//internalViewRefreshIsVisible()//
/**
* Sets the type of object serviced by this dynamic menu definition.
* @param type The serviced object type.
*/
public void setType(Class type) {
if(!isInitialized()) {
this.type = type;
}//if//
}//setType()//
/**
* Sets an association container used to access the text.
* @param container The text association metadata.
*/
public void setTextAssociation(MultiAssociationContainer container) {
verifyThread();
this.text.setAssociations(container);
}//setTextAssociation()//
/**
* Sets an association container used to access the image.
* @param container The image association metadata.
*/
public void setImageAssociation(MultiAssociationContainer container) {
verifyThread();
this.image.setAssociations(container);
}//setImageAssociation()//
/**
* Sets the association container used to access whether the menu is enabled.
* @param container The enabled state association metadata.
*/
public void setIsEnabledAssociation(MultiAssociationContainer container) {
verifyThread();
this.isEnabled.setAssociations(container);
}//setIsEnabledAssociation()//
/**
* Sets the association container used to access whether the menu is visible.
* @param container The visible state association metadata.
*/
public void setIsVisibleAssociation(MultiAssociationContainer container) {
verifyThread();
this.isVisible.setAssociations(container);
}//setIsVisibleAssociation()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#getShell()
*/
public Shell getShell() {
return DynamicMenu.this.getShell();
}//getShell()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#initializeControl(int)
*/
protected void initializeControl(int style, Object data) {
}//initializeControl()//
/* (non-Javadoc)
* @see com.foundation.view.IAbstractComponent#getName()
*/
public String getName() {
return DynamicMenu.this.getName();
}//getName()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#getViewContext()
*/
public IViewContext getViewContext() {
return DynamicMenu.this.getViewContext();
}//getViewContext()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#getResourceService()
*/
public AbstractResourceService getResourceService() {
return DynamicMenu.this.getResourceService();
}//getResourceService()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#getContainer()
*/
public IAbstractContainer getContainer() {
return DynamicMenu.this.getContainer();
}//getContainer()//
}//DynamicMenuDefinition//
public class CascadeMenuDefinition extends DynamicMenuDefinition {
/** The association providing a collection of objects that can be translated into menus via the menu definitions. */
private MultiResourceAssociation collection = new MultiResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_OBJECT, false, null);
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewInitialize()
*/
protected void internalViewInitialize() {
collection.initialize();
super.internalViewInitialize();
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewRelease()
*/
protected void internalViewRelease() {
collection.release();
super.internalViewRelease();
}//internalViewRelease()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalViewInitialize(java.lang.Object)
*/
protected void internalViewInitialize(Object value) {
collection.registerItem(value);
super.internalViewInitialize(value);
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalViewRefresh(java.lang.Object)
*/
protected void internalViewRefresh(Object alteredItem) {
internalViewRefreshCollection(alteredItem);
super.internalViewRefresh(alteredItem);
}//internalViewRefresh()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalViewRelease(java.lang.Object)
*/
protected void internalViewRelease(Object value) {
collection.unregisterItem(value);
super.internalViewRelease(value);
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenuData()
*/
public DynamicMenuData createMenuData() {
return new CascadeMenuData();
}//createMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#initializeMenuData(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void initializeMenuData(DynamicMenuData dynamicMenuData) {
collection.registerItem(dynamicMenuData.value);
}//initializeMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#releaseMenuData(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void releaseMenuData(DynamicMenuData dynamicMenuData) {
collection.unregisterItem(dynamicMenuData.value);
}//releaseMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void createMenu(DynamicMenuData dynamicMenuData) {
if(dynamicMenuData.isVisibleValue) {
CascadeMenuData menuData = (CascadeMenuData) dynamicMenuData;
org.eclipse.swt.widgets.Menu parentMenu = menuData.parent != null ? menuData.parent.swtMenu : DynamicMenu.this.getParent().getSwtMenu();
menuData.swtMenuItem = new org.eclipse.swt.widgets.MenuItem(parentMenu, SWT.CASCADE);
menuData.swtMenu = new org.eclipse.swt.widgets.Menu(parentMenu);
menuData.swtMenuItem.setMenu(menuData.swtMenu);
super.createMenu(dynamicMenuData);
menuData.swtMenu.setEnabled(dynamicMenuData.isEnabledValue);
}//if//
}//initializeMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#releaseMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void releaseMenu(DynamicMenuData dynamicMenuData) {
CascadeMenuData menuData = (CascadeMenuData) dynamicMenuData;
if(menuData.swtMenu != null) {
if(!menuData.swtMenu.isDisposed()) {
menuData.swtMenu.dispose();
}//if//
menuData.swtMenu = null;
}//if//
if(menuData.swtMenuItem != null) {
if(!menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.dispose();
}//if//
menuData.swtMenuItem = null;
}//if//
}//releaseMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalResourceHolderChanged(com.foundation.view.swt.MultiResourceHolder, java.lang.Object, java.lang.Object, java.lang.Object)
*/
protected void internalResourceHolderChanged(MultiResourceHolder resource, Object row, Object oldValue, Object newValue) {
if(resource == isVisibleHolder) {
CascadeMenuData menuData = (CascadeMenuData) getMenuData(row);
if(menuData != null) {
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
//menuData.swtMenuItem.setVisible(((Boolean) newValue).booleanValue());
}//if//
if(menuData.swtMenu != null && !menuData.swtMenu.isDisposed()) {
menuData.swtMenu.setVisible(((Boolean) newValue).booleanValue());
}//if//
}//if//
}//if//
else if(resource == isEnabledHolder) {
CascadeMenuData menuData = (CascadeMenuData) getMenuData(row);
if(menuData != null) {
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.setEnabled(((Boolean) newValue).booleanValue());
}//if//
if(menuData.swtMenu != null && !menuData.swtMenu.isDisposed()) {
menuData.swtMenu.setEnabled(((Boolean) newValue).booleanValue());
}//if//
}//if//
}//else if//
else {
super.internalResourceHolderChanged(resource, row, oldValue, newValue);
}//else//
}//internalOnAssociationChanged()//
/**
* Sets the association container used to access the collection of objects that are translated into menus.
* @param container The collection association metadata.
*/
public void setCollectionAssociation(MultiAssociationContainer container) {
verifyThread();
this.collection.setAssociations(container);
}//setCollectionAssociation()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalOnValueChanged(com.foundation.view.MultiResourceAssociation, java.lang.Object, boolean)
*/
protected void internalOnValueChanged(MultiResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isUpdate) {
if(resourceAssociation == collection) {
internalViewRefreshCollection(alteredItem);
}//if//
else {
super.internalOnValueChanged(resourceAssociation, alteredItem, data, isUpdate);
}//else//
}//internalOnValueChanged()//
/**
* Refreshes the collection.
*/
protected void internalViewRefreshCollection(Object alteredItem) {
if(collection.refresh(alteredItem)) {
CascadeMenuData menuData = (CascadeMenuData) getMenuData(alteredItem);
ICollection newCollection;
CollectionContainer container = menuData.collectionContainer;
Object value = collection.getValue(alteredItem);
if((value == null) || (value instanceof ICollection)) {
newCollection = (ICollection) value;
}//if//
else if(value instanceof Object[]) {
newCollection = new LiteList((Object[]) value);
}//else if//
else {
newCollection = new LiteList(value);
}//else//
if(container != null) {
container.release();
container = null;
menuData.collectionContainer = null;
}//if//
if(newCollection != null) {
container = new CollectionContainer(newCollection, menuData);
container.initialize();
menuData.collectionContainer = container;
}//if//
}//if//
}//internalViewRefreshCollection()//
}//CascadeMenuDefinition//
public class PushMenuDefinition extends DynamicMenuDefinition {
/** Whether the method called when the menu is pressed should be performed on the UI thread. */
private boolean synchronousSelection = false;
/** Called when the menu is pressed. */
private IMethodAssociation selectionMethod = null;
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenuData()
*/
public DynamicMenuData createMenuData() {
return new PushToggleMenuData();
}//createMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#initializeMenuData(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void initializeMenuData(DynamicMenuData dynamicMenuData) {
//Does nothing.//
}//initializeMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#releaseMenuData(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void releaseMenuData(DynamicMenuData dynamicMenuData) {
//Does nothing.//
}//releaseMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void createMenu(DynamicMenuData dynamicMenuData) {
final PushToggleMenuData menuData = (PushToggleMenuData) dynamicMenuData;
org.eclipse.swt.widgets.Menu parentMenu = menuData.parent != null ? menuData.parent.swtMenu : DynamicMenu.this.getParent().getSwtMenu();
//TODO: Ensure that the menu item is created at the correct index within the parent. May need to pass an index into this method? May need to recreate the whole stinking swt menu?
menuData.swtMenuItem = new org.eclipse.swt.widgets.MenuItem(parentMenu, SWT.PUSH);
menuData.swtMenuItem.addSelectionListener(new SelectionListener() {
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent event) {
}//widgetDefaultSelected()//
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent event) {
if(selectionMethod != null) {
selectionMethod.invoke(new Object[] {menuData.value}, synchronousSelection);
}//if//
}//widgetSelected()//
});
super.createMenu(dynamicMenuData);
}//initializeMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#releaseMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void releaseMenu(DynamicMenuData dynamicMenuData) {
PushToggleMenuData menuData = (PushToggleMenuData) dynamicMenuData;
if(menuData.swtMenuItem != null) {
if(!menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.dispose();
}//if//
menuData.swtMenuItem = null;
}//if//
}//releaseMenuData()//
/**
* Sets the selection method called when the menu is pressed.
* @param selectionMethod The method called when the menu is selected.
*/
public void setSelectionMethod(IMethodAssociation selectionMethod) {
verifyThread();
this.selectionMethod = selectionMethod;
}//setSelectionMethod()//
/**
* Sets whether the call to update the selection is synchronous.
* @param synchronousSelection Whether the call to the selection method is synchronous.
*/
public void setSynchronousSelection(boolean synchronousSelection) {
this.synchronousSelection = synchronousSelection;
}//setSynchronousSelection()//
}//PushMenuDefinition//
public class ToggleMenuDefinition extends DynamicMenuDefinition {
/** The menu's selection state resource. */
protected MultiResourceAssociation selection = new MultiResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_BOOLEAN, true, Boolean.FALSE);
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewInitialize()
*/
protected void internalViewInitialize() {
selection.initialize();
super.internalViewInitialize();
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewRelease()
*/
protected void internalViewRelease() {
selection.release();
super.internalViewRelease();
}//internalViewRelease()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalViewInitialize(java.lang.Object)
*/
protected void internalViewInitialize(Object value) {
selection.registerItem(value);
super.internalViewInitialize(value);
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalViewRefresh(java.lang.Object)
*/
protected void internalViewRefresh(Object alteredItem) {
internalViewRefreshSelection(alteredItem);
super.internalViewRefresh(alteredItem);
}//internalViewRefresh()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalViewRelease(java.lang.Object)
*/
protected void internalViewRelease(Object value) {
selection.unregisterItem(value);
super.internalViewRelease(value);
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenuData()
*/
public DynamicMenuData createMenuData() {
return new PushToggleMenuData();
}//createMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#initializeMenuData(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void initializeMenuData(DynamicMenuData dynamicMenuData) {
selection.registerItem(dynamicMenuData.value);
}//initializeMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#releaseMenuData(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void releaseMenuData(DynamicMenuData dynamicMenuData) {
selection.unregisterItem(dynamicMenuData.value);
}//releaseMenuData()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#releaseMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void releaseMenu(DynamicMenuData dynamicMenuData) {
PushToggleMenuData menuData = (PushToggleMenuData) dynamicMenuData;
if(menuData.swtMenuItem != null) {
if(!menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.dispose();
}//if//
menuData.swtMenuItem = null;
}//if//
}//releaseMenu()//
/**
* Sets an association container used to access the selection.
* @param container The selection association metadata.
*/
public void setSelectionAssociation(MultiAssociationContainer container) {
verifyThread();
this.selection.setAssociations(container);
}//setSelectionAssociation()//
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#internalOnValueChanged(com.foundation.view.MultiResourceAssociation, java.lang.Object, boolean)
*/
protected void internalOnValueChanged(MultiResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isUpdate) {
if(resourceAssociation == selection) {
internalViewRefreshSelection(alteredItem);
}//if//
else {
super.internalOnValueChanged(resourceAssociation, alteredItem, data, isUpdate);
}//else//
}//internalOnValueChanged()//
/**
* Refreshes the selection.
*/
protected void internalViewRefreshSelection(Object alteredItem) {
if(selection.refresh(alteredItem)) {
Boolean value = (Boolean) selection.getValue(alteredItem);
PushToggleMenuData menuData = (PushToggleMenuData) getMenuData(alteredItem);
menuData.isSelectedValue = value.booleanValue();
if(menuData.swtMenuItem != null && !menuData.swtMenuItem.isDisposed()) {
menuData.swtMenuItem.setSelection(menuData.isSelectedValue);
}//if//
}//if//
}//internalViewRefreshSelection()//
}//ToggleMenuDefinition//
public class CheckMenuDefinition extends ToggleMenuDefinition {
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void createMenu(DynamicMenuData dynamicMenuData) {
final PushToggleMenuData menuData = (PushToggleMenuData) dynamicMenuData;
org.eclipse.swt.widgets.Menu parentMenu = menuData.parent != null ? menuData.parent.swtMenu : DynamicMenu.this.getParent().getSwtMenu();
//TODO: Ensure that the menu item is created at the correct index within the parent. May need to pass an index into this method? May need to recreate the whole stinking swt menu?
menuData.swtMenuItem = new org.eclipse.swt.widgets.MenuItem(parentMenu, SWT.CHECK);
menuData.swtMenuItem.setSelection(menuData.isSelectedValue);
menuData.swtMenuItem.addSelectionListener(new SelectionListener() {
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent event) {
}//widgetDefaultSelected()//
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent event) {
menuData.isSelectedValue = ((MenuItem) event.widget).getSelection();
selection.setValue(menuData.value, menuData.isSelectedValue ? Boolean.TRUE : Boolean.FALSE);
}//widgetSelected()//
});
super.createMenu(dynamicMenuData);
menuData.swtMenuItem.setSelection(menuData.isSelectedValue);
}//createMenu()//
}//CheckMenuDefinition//
public class RadioMenuDefinition extends ToggleMenuDefinition {
/* (non-Javadoc)
* @see com.foundation.view.swt.DynamicMenu.DynamicMenuDefinition#createMenu(com.foundation.view.swt.DynamicMenu.DynamicMenuData)
*/
public void createMenu(DynamicMenuData dynamicMenuData) {
final PushToggleMenuData menuData = (PushToggleMenuData) dynamicMenuData;
org.eclipse.swt.widgets.Menu parentMenu = menuData.parent != null ? menuData.parent.swtMenu : DynamicMenu.this.getParent().getSwtMenu();
//TODO: Ensure that the menu item is created at the correct index within the parent. May need to pass an index into this method? May need to recreate the whole stinking swt menu?
menuData.swtMenuItem = new org.eclipse.swt.widgets.MenuItem(parentMenu, SWT.RADIO);
menuData.swtMenuItem.setSelection(menuData.isSelectedValue);
menuData.swtMenuItem.addSelectionListener(new SelectionListener() {
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent event) {
}//widgetDefaultSelected()//
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent event) {
menuData.isSelectedValue = ((MenuItem) event.widget).getSelection();
selection.setValue(menuData.value, menuData.isSelectedValue ? Boolean.TRUE : Boolean.FALSE);
}//widgetSelected()//
});
super.createMenu(dynamicMenuData);
menuData.swtMenuItem.setSelection(menuData.isSelectedValue);
}//createMenu()//
}//RadioMenuDefinition//
/**
* The base class for the menu data tree.
*/
public class DynamicMenuData {
/** The value that created the menu. */
public Object value = null;
/** The parent menu data, or null if the menu is a root menu (within the context of the dynamic menu control). */
public CascadeMenuData parent = null;
/** The SWT menu item resource. */
public org.eclipse.swt.widgets.MenuItem swtMenuItem = null;
/** The menu's definition that created it. */
public DynamicMenuDefinition definition = null;
/** The current value for the visibility of the menu. This stores the visiblity even if the menu control is not currently created. */
public boolean isVisibleValue = true;
/** The current value for the enabled state of the menu. This stores the enabled state even if the menu control is not currently created. */
public boolean isEnabledValue = true;
/** The current value for the selection state of the menu. This stores the selection state even if the menu control is not currently created. */
public boolean isSelectedValue = false;
/** The current value for the menu's text. This stores the text even if the menu control is not currently created. */
public String textValue = "";
/** The current value for the menu's image. This stores the image even if the menu control is not currently created. */
public JefImage imageValue = null;
}//DynamicMenuData//
/**
* Menu data specific to the cascade menu definition.
*/
public class CascadeMenuData extends DynamicMenuData {
/** The container for the collection of child values and the linkage to receive updates from the collection. */
public CollectionContainer collectionContainer = null;
/** The SWT resource for the menu that can contain children. */
public org.eclipse.swt.widgets.Menu swtMenu = null;
}//CascadeMenuData//
/**
* Menu data specific to the push or toggle menu definition.
*/
public class PushToggleMenuData extends DynamicMenuData {
}//PushToggleMenuData//
/**
* DynamicMenu constructor.
* @param parent The parent menu.
* @param name The name for the menu.
* @param style The style for the menu. Currently no styles are defined for this component.
*/
public DynamicMenu(Menu parent, String name, int style) {
super(parent.getContainer(), style);
this.parent = parent;
this.name = name;
parent.addSubMenu(this);
}//DynamicMenu()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#initializeControl(int)
*/
protected void initializeControl(int style, Object data) {
}//initializeControl()//
/**
* Creates the menu definition object given the class.
* @param cls The class for the type of menu definition to create.
* @return The menu definition object.
*/
public DynamicMenuDefinition createMenuDefinition(Class cls) {
DynamicMenuDefinition result = null;
verifyThread();
if(!isInitialized()) {
if(cls.equals(CascadeMenuDefinition.class)) {
result = new CascadeMenuDefinition();
}//if//
else if(cls.equals(PushMenuDefinition.class)) {
result = new PushMenuDefinition();
}//else if//
else if(cls.equals(CheckMenuDefinition.class)) {
result = new CheckMenuDefinition();
}//else if//
else if(cls.equals(RadioMenuDefinition.class)) {
result = new RadioMenuDefinition();
}//else if//
getDefinitions().add(result);
}//if//
return result;
}//createMenuDefinition()//
/**
* Gets the menu definitions in the order to be searched.
* @return The menu definitions which are used to build menus given model objects.
*/
protected IList getDefinitions() {
return definitions;
}//getDefinitions()//
/**
* Gets the menu that is the parent of this component.
* @return The parent menu.
*/
protected Menu getParent() {
return parent;
}//getParent()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#getShell()
*/
public Shell getShell() {
return getParent() != null ? getParent().getShell() : null;
}//getShell()//
/* (non-Javadoc)
* @see com.foundation.view.IAbstractComponent#getName()
*/
public String getName() {
return name;
}//getName()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewInitialize()
*/
protected void internalViewInitialize() {
for(int index = 0; index < definitions.getSize(); index++) {
((DynamicMenuDefinition) definitions.get(index)).internalViewInitializeAll();
}//for//
collection.initialize();
super.internalViewInitialize();
}//internalViewInitialize()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewRefresh()
*/
protected void internalViewRefresh() {
super.internalViewRefresh();
for(int index = 0; index < getDefinitions().getSize(); index++) {
DynamicMenuDefinition definition = (DynamicMenuDefinition) getDefinitions().get(index);
definition.internalViewRefreshAll();
}//for//
}//internalViewRefresh()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewRelease()
*/
protected void internalViewRelease() {
collection.release();
for(int index = 0; index < definitions.getSize(); index++) {
((DynamicMenuDefinition) definitions.get(index)).internalViewReleaseAll();
}//for//
super.internalViewRelease();
}//internalViewRelease()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalViewSynchronize()
*/
protected void internalViewSynchronize() {
super.internalViewSynchronize();
}//internalViewSynchronize()//
/**
* Sets the association container used to access the collection of objects that are translated into menus.
* @param container The collection association metadata.
*/
public void setCollectionAssociation(SingleAssociationContainer container) {
verifyThread();
this.collection.setAssociations(container);
}//setCollectionAssociation()//
/* (non-Javadoc)
* @see com.foundation.view.swt.AbstractComponent#internalOnValueChanged(com.foundation.view.SingleResourceAssociation, boolean)
*/
protected void internalOnValueChanged(SingleResourceAssociation resourceAssociation, int flags) {
if(resourceAssociation == collection) {
internalViewRefreshCollection();
}//if//
else {
super.internalOnValueChanged(resourceAssociation, flags);
}//else//
}//internalOnValueChanged()//
/**
* Refreshes the collection.
*/
protected void internalViewRefreshCollection() {
if(collection.refresh()) {
ICollection newCollection;
if((collection.getValue() == null) || (collection.getValue() instanceof ICollection)) {
newCollection = (ICollection) collection.getValue();
}//if//
else if(collection.getValue() instanceof Object[]) {
newCollection = new LiteList((Object[]) collection.getValue());
}//else if//
else {
newCollection = new LiteList(collection.getValue());
}//else//
if(rootContainer != null) {
rootContainer.release();
rootContainer = null;
}//if//
if(newCollection != null) {
rootContainer = new CollectionContainer(newCollection, null);
rootContainer.initialize();
}//if//
}//if//
}//internalViewRefreshCollection()//
/* (non-Javadoc)
* @see com.foundation.view.swt.IAbstractMenu#loadMenu()
*/
public void loadMenu() {
}//loadMenu()//
/* (non-Javadoc)
* @see com.foundation.view.swt.IAbstractMenu#unloadMenu()
*/
public void unloadMenu() {
}//loadMenu()//
/* (non-Javadoc)
* @see com.foundation.view.swt.IAbstractMenu#loadMenuChildren()
*/
public void loadMenuChildren() {
loadMenus(rootContainer);
}//loadMenuChildren()//
/**
* Loads the menus for each object in the container.
* @param container The container for the objects that are translated into menus.
*/
public void loadMenus(CollectionContainer container) {
for(IIterator iterator = container.collection.iterator(); iterator.hasNext(); ) {
Object value = iterator.next();
DynamicMenuData menuData = (DynamicMenuData) menuDataMap.get(value);
if(menuData != null) {
menuData.definition.createMenu(menuData);
if(menuData instanceof CascadeMenuData && ((CascadeMenuData) menuData).collectionContainer != null) {
loadMenus(((CascadeMenuData) menuData).collectionContainer);
}//if//
}//if//
}//for//
}//loadMenus()//
/* (non-Javadoc)
* @see com.foundation.view.swt.IAbstractMenu#unloadMenuChildren()
*/
public void unloadMenuChildren() {
unloadMenus(rootContainer);
}//unloadMenuChildren()//
/**
* Unloads the menus for each object in the container.
* @param container The container for the objects that are translated into menus.
*/
public void unloadMenus(CollectionContainer container) {
for(IIterator iterator = container.collection.iterator(); iterator.hasNext(); ) {
Object value = iterator.next();
DynamicMenuData menuData = (DynamicMenuData) menuDataMap.get(value);
if(menuData != null) {
if(menuData instanceof CascadeMenuData && ((CascadeMenuData) menuData).collectionContainer != null) {
unloadMenus(((CascadeMenuData) menuData).collectionContainer);
}//if//
menuData.definition.releaseMenu(menuData);
}//if//
}//for//
}//unloadMenus()//
}//DynamicMenu//