/* * Copyright (c) 2003,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.tcv.swt.server; import com.common.util.*; import com.common.debug.*; import com.foundation.view.*; import com.foundation.view.resource.ResourceReference; import com.foundation.tcv.model.LinkInfo; import com.foundation.tcv.swt.IMenu; import com.foundation.tcv.view.*; public class Menu extends AbstractComponent implements IMenu { /** The menu's parent component which may be another menu. */ private AbstractComponent parent = null; /** An internal flag to assist in determining whether this menu is allowed to have sub-menus. */ private boolean canHaveSubMenus = false; /** A collection of sub menus. */ private IList subMenus = null; /** The menu's name. */ private String name = null; /** Called when the button is pressed. */ private IMethodAssociation selectionMethod = null; /** The menu's selection state resource. */ private SingleResourceAssociation selection; /** The menu's title resource. */ private SingleResourceAssociation text; /** The menu's image resource. */ private SingleResourceAssociation image; /** The resource defining the visible state for the menu. */ private SingleResourceAssociation isVisible; /** The resource defining the enabled state for the menu. */ private SingleResourceAssociation isEnabled; /** The current accelerator character used to activate the menu. */ private Integer currentAccelerator = null; /** Whether the menu is a push menu which doesn't retain a selection state. */ private final boolean isPushMenu; /** Whether the menu is a radio or check style menu which retains a selection state. */ private final boolean isRadioCheckMenu; /** Whether the method called when the menu is pressed should be performed on the UI thread. */ private boolean synchronousSelection = false; /** A reusable single value array. */ private Object[] reusableArrayOne = new Object[1]; /** * Menu constructor. * @param parent The component containing the menu. There are only a few components that allow a menu bar. All components may have one popup menu. * @param name The optional name for the menu. * @param style The menu style. The menu may be a bar, popup, or cascade if it contains other menus. * @see #STYLE_BAR * @see #STYLE_POPUP * @see #STYLE_CASCADE * @see #STYLE_RADIO * @see #STYLE_CHECK * @see #STYLE_PUSH * @see #STYLE_SEPARATOR * @see #STYLE_RIGHT_TO_LEFT * @see #STYLE_LEFT_TO_RIGHT */ public Menu(com.foundation.view.IAbstractComponent parent, String name, int style) { super(((AbstractComponent) parent).getSessionViewController()); this.parent = (AbstractComponent) parent; this.name = name; this.isPushMenu = (style & STYLE_PUSH) > 0; this.isRadioCheckMenu = ((style & STYLE_RADIO) > 0) || ((style & STYLE_CHECK) > 0); if(!(((style & STYLE_BAR) > 0) || ((style & STYLE_POPUP) > 0))) { throw new IllegalArgumentException("The parent must be another menu for this menu style."); }//if// canHaveSubMenus = true; if((style & STYLE_BAR) > 0) { if(parent instanceof IDecoration) { ((IDecoration) parent).setMenuBar(this); }//if// else { Debug.log("Error: Cannot assign a menu bar to a non-IDecoration component."); }//else// }//if// else if((style & STYLE_POPUP) > 0) { if(this.parent instanceof AbstractComponent) { ((AbstractComponent) this.parent).setMenu(this); }//if// }//if// initialize(); //Initialize the client control.// sendMessage(MESSAGE_INITIALIZE, null, null, this.parent.getNumber(), style); }//Menu()// /** * Menu constructor. * @param parent The menu containing the menu. * @param name The optional name for the menu. * @param style The menu style. The menu may be a bar, popup, or cascade if it contains other menus. * @see #STYLE_BAR * @see #STYLE_POPUP * @see #STYLE_CASCADE * @see #STYLE_RADIO * @see #STYLE_CHECK * @see #STYLE_PUSH * @see #STYLE_SEPARATOR * @see #STYLE_RIGHT_TO_LEFT * @see #STYLE_LEFT_TO_RIGHT */ public Menu(Menu parent, String name, int style) { super(parent.getSessionViewController()); this.parent = parent; this.name = name; this.isPushMenu = (style & STYLE_PUSH) > 0; this.isRadioCheckMenu = ((style & STYLE_RADIO) > 0) || ((style & STYLE_CHECK) > 0); if(((style & STYLE_BAR) > 0) || ((style & STYLE_POPUP) > 0)) { throw new IllegalArgumentException("The parent may not be another menu for this menu style."); }//if// if((style & STYLE_CASCADE) > 0) { canHaveSubMenus = true; }//if// parent.getSubMenus().add(this); initialize(); sendMessage(MESSAGE_INITIALIZE, null, null, parent.getNumber(), style); }//Menu()// /** * Initializes the view component. */ protected void initialize() { selection = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_BOOLEAN, true, Boolean.FALSE); text = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_TEXT, false, ""); image = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_IMAGE, false, null); isVisible = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_BOOLEAN, false, Boolean.TRUE); isEnabled = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_BOOLEAN, false, Boolean.TRUE); }//initialize()// /** * Gets the collection of all sub menus. * @return A collection of menus, or null if this menu cannot hold other menus. */ private IList getSubMenus() { if((canHaveSubMenus) && (subMenus == null)) { subMenus = new LiteList(4, 8); }//if// return subMenus; }//getSubMenus()// /** * Gets the parent component for this component. * @return The view component containing this component. This will not be null, and may be a Component or another Menu instance. */ public AbstractComponent getParent() { return parent; }//getParent()// /** * Gets the container nearest the menu. * @return The nearest container to this menu. */ public IAbstractContainer getContainer() { AbstractComponent component = getParent(); while(component instanceof Menu) { component = ((Menu) component).getParent(); }//while// return component instanceof IAbstractContainer ? (IAbstractContainer) component : ((IAbstractComponent) component).getContainer(); }//getContainer()// /** * Gets the component's name. * @return The name of this component. */ public String getName() { return name; }//getName()// /** * Gets the accelerator character for the menu. * @return The menu accelerator key. */ public int getAccelerator() { verifyThread(); return currentAccelerator.intValue(); }//setAccelerator()// /** * Sets the accelerator character for the menu. * @param accelerator The menu accelerator key. */ public void setAccelerator(int accelerator) { verifyThread(); if((currentAccelerator == null) || (currentAccelerator.intValue() != accelerator)) { currentAccelerator = new Integer(accelerator); sendMessage(MESSAGE_SET_ACCELERATOR, currentAccelerator); }//if// }//setAccelerator()// /** * Gets the component selection state. * @return Whether the component is in the selected state. */ public Boolean getIsSelected() { verifyThread(); return (Boolean) this.selection.getDefaultValue(); }//getIsSelected()// /** * Sets the menu's default selection state. * @param isSelected Whether the menu is selected by default. */ public void setIsSelected(Boolean isSelected) { verifyThread(); if(isRadioCheckMenu) { this.selection.setDefaultValue(isSelected); }//if// }//setIsSelected()// /** * Sets the selection method called when the menu is pressed (intended for push menus). *
TODO: Currently this may not be changed after initializing the control. Make this changeable? or throw an error if it is changed?
* @param selectionMethod The method called when the menu is selected. */ public void setSelectionMethod(IMethodAssociation selectionMethod) { verifyThread(); if(isPushMenu) { this.selectionMethod = selectionMethod; }//if// }//setSelectionMethod()// /** * Adds a link for the selection. * @param link The local linkage for the selection. */ public void addSelectionLink(LinkData link) { sendMessage(MESSAGE_ADD_SELECTION_LINK, new LinkInfo(((AbstractComponent) link.getComponent()).getNumber(), link.getTarget(), link.getData(), link.isBoolean(), link.invertLogic(), link.nullValue())); }//addSelectionLink()// /** * Sets an association container used to access the text. * @param container The text association metadata. */ public void setTextAssociation(SingleAssociationContainer 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(SingleAssociationContainer container) { verifyThread(); this.image.setAssociations(container); }//setImageAssociation()// /** * Sets an association container used to access the selection. * @param container The selection association metadata. */ public void setSelectionAssociation(SingleAssociationContainer container) { verifyThread(); this.selection.setAssociations(container); }//setSelectionAssociation()// /** * Sets the association container used to access whether the menu is enabled. * @param container The enabled state association metadata. */ public void setIsEnabledAssociation(SingleAssociationContainer 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(SingleAssociationContainer container) { verifyThread(); this.isVisible.setAssociations(container); }//setIsVisibleAssociation()// /** * Sets whether the selection synchronization should wait for a reply before returning control to the user. * @param synchronousSelection Whether the selection is synchronous. */ public void setSynchronousSelection(boolean synchronousSelection) { verifyThread(); if(isPushMenu) { if(this.synchronousSelection != synchronousSelection) { this.synchronousSelection = synchronousSelection; sendMessage(MESSAGE_SET_SYNCHRONOUS_SELECTION, synchronousSelection ? Boolean.TRUE : Boolean.FALSE); }//if// }//if// }//setSynchronousSelection()// /** * Sets the component text. * @param text The text that will appear in the component. */ public void setText(String text) { verifyThread(); this.text.setDefaultValue(text); }//setText()// /** * Sets the component's default text resource. * @param text The text to be displayed by this component. */ public void setText(ResourceReference text) { verifyThread(); this.text.setDefaultValue(text); }//setText()// /** * Sets the component image. * @param image The image to be displayed by the component. */ public void setImage(JefImage image) { verifyThread(); this.image.setDefaultValue(image); }//setImage()// /** * Sets the component's default image resource. * @param image The image to be displayed by this component. */ public void setImage(ResourceReference image) { verifyThread(); this.image.setDefaultValue(image); }//setImage()// /** * Sets whether the menu is visible by default. * @param isVisible The default visiblity for the menu. */ public void setIsVisible(boolean isVisible) { verifyThread(); this.isVisible.setDefaultValue(isVisible ? Boolean.TRUE : Boolean.FALSE); }//setIsVisible()// /** * Sets the default visibility resource. * @param isVisible Whether the user can see the component. */ public void setIsVisible(ResourceReference isVisible) { verifyThread(); this.isVisible.setDefaultValue(isVisible); }//setIsVisible()// /** * Sets the default enabled state of the menu. * @param isEnabled Whether the menu is enabled by default. */ public void setIsEnabled(boolean isEnabled) { verifyThread(); this.isEnabled.setDefaultValue(isEnabled ? Boolean.TRUE : Boolean.FALSE); }//setIsEnabled()// /** * Sets the default enabled state resource. * @param isEnabled Whether the user can interact with the component. */ public void setIsEnabled(ResourceReference isEnabled) { verifyThread(); this.isEnabled.setDefaultValue(isEnabled); }//setIsEnabled()// /** * Sets the menu location. * @param x The upper left point's X coordinate. * @param y The upper left point's Y coordinate. */ public void setLocation(int x, int y) { verifyThread(); sendMessage(MESSAGE_SET_LOCATION, new int[] {x, y}); }//setLocation()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalViewInitializeAll() */ protected void internalViewInitializeAll() { super.internalViewInitializeAll(); try { //Initialize sub-menus.// if((getSubMenus() != null) && (getSubMenus().getSize() > 0)) { IIterator iterator = getSubMenus().iterator(); while(iterator.hasNext()) { Menu next = (Menu) iterator.next(); next.internalViewInitializeAll(); }//while// }//if// }//try// catch(Throwable e) { Debug.log(e); }//catch// }//internalViewInitializeAll()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalViewReleaseAll() */ protected void internalViewReleaseAll() { super.internalViewReleaseAll(); try { //Release sub-menus.// if((getSubMenus() != null) && (getSubMenus().getSize() > 0)) { IIterator iterator = getSubMenus().iterator(); while(iterator.hasNext()) { Menu next = (Menu) iterator.next(); next.internalViewReleaseAll(); }//while// }//if// }//try// catch(Throwable e) { Debug.log(e); }//catch// }//internalViewReleaseAll()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalViewRefreshAll() */ protected void internalViewRefreshAll() { //Refresh this component.// super.internalViewRefreshAll(); try { //Refresh sub-menus.// if((getSubMenus() != null) && (getSubMenus().getSize() > 0)) { IIterator iterator = getSubMenus().iterator(); while(iterator.hasNext()) { Menu next = (Menu) iterator.next(); next.internalViewRefreshAll(); }//while// }//if// }//try// catch(Throwable e) { Debug.log(e); }//catch// }//internalViewRefreshAll()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalViewInitialize() */ protected void internalViewInitialize() { text.initialize(); image.initialize(); isVisible.initialize(); isEnabled.initialize(); selection.initialize(); super.internalViewInitialize(); }//internalViewInitialize()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalViewRelease() */ protected void internalViewRelease() { text.release(); image.release(); isVisible.release(); isEnabled.release(); selection.release(); super.internalViewRelease(); }//internalViewRelease()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalViewRefresh() */ protected void internalViewRefresh() { addMessageHold(); try { internalViewRefreshSelection(); internalViewRefreshText(); internalViewRefreshImage(); internalViewRefreshIsVisible(); internalViewRefreshIsEnabled(); super.internalViewRefresh(); }//try// finally { removeMessageHold(); }//finally// }//internalViewRefresh()// /** * Refreshes the selection. */ protected void internalViewRefreshSelection() { if(selection.refresh()) { sendMessage(MESSAGE_SET_IS_SELECTED, selection.getValue()); }//if// }//internalViewRefreshSelection()// /** * Refreshes the text from the text attribute and the default text. */ protected void internalViewRefreshText() { if(text.refresh()) { sendMessage(MESSAGE_SET_TEXT, (String) text.getValue()); }//if// }//internalViewRefreshText()// /** * Refreshes the image from the image url attribute and the default image url. */ protected void internalViewRefreshImage() { if(image.refresh()) { sendMessage(MESSAGE_SET_IMAGE, (JefImage) image.getValue()); }//if// }//internalViewRefreshImage()// /** * Refreshes whether the menu is enabled based on the attribute value or if null the default flag value. */ protected void internalViewRefreshIsEnabled() { if(isEnabled.refresh()) { sendMessage(MESSAGE_SET_IS_ENABLED, isEnabled.getValue() == null ? Boolean.TRUE : (Boolean) isEnabled.getValue()); }//if// }//internalViewRefreshIsEnabled()// /** * Refreshes whether the menu is visible based on the attribute value or if null the default flag value. */ protected void internalViewRefreshIsVisible() { if(isVisible.refresh()) { sendMessage(MESSAGE_SET_IS_VISIBLE, isVisible.getValue() == null ? Boolean.TRUE : (Boolean) isVisible.getValue()); }//if// }//internalViewRefreshIsVisible()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalOnValueChanged(com.foundation.view.SingleResourceAssociation) */ protected void internalOnValueChanged(SingleResourceAssociation resourceAssociation, int flags) { if(resourceAssociation == isVisible) { internalViewRefreshIsVisible(); }//if// else if(resourceAssociation == isEnabled) { internalViewRefreshIsEnabled(); }//else if// else if(resourceAssociation == text) { internalViewRefreshText(); }//else if// else if(resourceAssociation == image) { internalViewRefreshImage(); }//else if// else if(resourceAssociation == selection) { internalViewRefreshSelection(); }//else if// else { super.internalOnValueChanged(resourceAssociation, flags); }//else// }//internalOnValueChanged()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#internalOnEventFired(com.foundation.tcv.swt.server.IEventAssociation, java.lang.Object[]) */ protected void internalOnEventFired(IEventAssociation eventAssociation, Object[] eventArguments) { super.internalOnEventFired(eventAssociation, eventArguments); }//internalOnEventFired()// /* (non-Javadoc) * @see com.foundation.tcv.view.IViewComponent#processMessage(com.foundation.tcv.model.ViewMessage) */ public Object processMessage(ViewMessage viewMessage) { Object retVal = null; switch(viewMessage.getMessageNumber()) { case MESSAGE_VIEW_SYNCHRONIZE_SELECTION: { Boolean selectionState = (Boolean) viewMessage.getMessageData(); //Call the selection method. This is usually set for push menus.// if((isPushMenu) && (selectionMethod != null)) { reusableArrayOne[0] = selectionState; selectionMethod.invoke(reusableArrayOne, synchronousSelection); }//if// //Call the selection attribute's setter. This is usually set for non-push menus.// if(isRadioCheckMenu) { selection.setValue(selectionState); }//if// break; }//case// default: { retVal = super.processMessage(viewMessage); }//default// }//switch// return retVal; }//processMessage()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.AbstractComponent#getClientClassName() */ protected String getClientClassName() { return "com.foundation.tcv.swt.client.Menu"; }//getClientClassName()// /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { return super.toString() + (getName() != null ? " (named: " + getName() + ")" : ""); }//toString()// }//Menu//