/* * 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.view.swt; import org.eclipse.swt.SWT; import org.eclipse.swt.events.*; import com.common.comparison.Comparator; import com.common.debug.*; import com.common.thread.IRunnable; import com.foundation.view.*; import com.foundation.view.resource.ResourceReference; import com.foundation.view.swt.util.SwtUtilities; public class Button extends Component implements SelectionListener { public static final int STYLE_ARROW = SWT.ARROW; public static final int STYLE_CHECK = SWT.CHECK; public static final int STYLE_PUSH = SWT.PUSH; public static final int STYLE_RADIO = SWT.RADIO; public static final int STYLE_TOGGLE = SWT.TOGGLE; public static final int STYLE_FLAT = SWT.FLAT; public static final int STYLE_LEFT = SWT.LEFT; public static final int STYLE_RIGHT = SWT.RIGHT; public static final int STYLE_CENTER = SWT.CENTER; public static final int STYLE_UP = SWT.UP; public static final int STYLE_DOWN = SWT.DOWN; public static final int LINK_TARGET_SELECTION = Component.LAST_LINK_TARGET + 1; public static final int LINK_TARGET_IMAGE = Component.LAST_LINK_TARGET + 2; public static final int LINK_TARGET_TEXT = Component.LAST_LINK_TARGET + 3; public static final int LAST_LINK_TARGET = Component.LAST_LINK_TARGET + 3; /** Called when the button is pressed (only used for push buttons). */ private IMethodAssociation selectionMethod = null; /** The button's selection state resource. */ private SingleResourceAssociation selection = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_OBJECT, true, Boolean.FALSE); /** The button's text resource. */ private SingleResourceAssociation text = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_TEXT, false, ""); /** The button's image resource. */ private SingleResourceAssociation image = new SingleResourceAssociation(this, this, getViewContext(), SingleResourceAssociation.TYPE_IMAGE, false, null); /** The linkage for the selection. */ private Linkage selectionLinkage = new Linkage(); /** The optional object that represents a selection of this button. Used for radio buttons only - to allow them to work on the same attribute. Never used for the linkages. */ private Object selectionData = null; /** Whether the selection state is auto synchronized. */ private boolean autoSynchronizeSelection = false; /** The delay to be used when auto synchronizing changes to the text. */ private long autoSynchronizeSelectionDelay = 500; /** The task that auto synchronizes the selection after a short delay. */ private Task autoSynchronizeSelectionTask = null; /** Whether this is a stateful button. */ private final boolean isStateful; /** Whether this is a radio button. */ private final boolean isRadio; /** A holder for the value of the text. */ private ResourceHolder textHolder = new ResourceHolder(this); /** A holder for the value of the image. */ private ResourceHolder imageHolder = new ResourceHolder(this); /** Whether the validation code in the view controller should be executed after synchronizing the state. */ private boolean autoValidate = false; /** * Button constructor. * @param parent A composite control which will be the parent of the new instance (cannot be null). * @param name The unique component name. * @param style The style of control to construct. * @see #STYLE_ARROW * @see #STYLE_CHECK * @see #STYLE_PUSH * @see #STYLE_RADIO * @see #STYLE_TOGGLE * @see #STYLE_FLAT * @see #STYLE_LEFT * @see #STYLE_RIGHT * @see #STYLE_CENTER * @see #STYLE_UP * @see #STYLE_DOWN */ public Button(Container parent, String name, int style) { super(parent, name, style); isRadio = (style & STYLE_RADIO) > 0; isStateful = isRadio || ((style & STYLE_TOGGLE) > 0) || ((style & STYLE_CHECK) > 0); }//Button()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#initializeControl(int) */ protected void initializeControl(int style, Object data) { setSwtWidget(new org.eclipse.swt.widgets.Button(((Container) getContainer()).getSwtParent(), style)); getSwtWidget().setData(this); }//initializeControl()// /** * Gets the SWT button that represents this button. * @return The SWT button providing visualization for this button. */ public org.eclipse.swt.widgets.Button getSwtButton() { return (org.eclipse.swt.widgets.Button) getSwtControl(); }//getSwtButton()// /** * Gets the optional selection data for the radio button (radio only). * @return The data to use to determine whether this button is selected (within the group of radio buttons) and to pass to the model when selected. */ public Object getSelectionData() { verifyThread(); return selectionData; }//getSelectionData()// /** * Sets the optional selection data for the radio button (radio only). * @param selectionData The data to use to determine whether this button is selected (within the group of radio buttons) and to pass to the model when selected. */ public void setSelectionData(Object selectionData) { verifyThread(); if(isRadio && !isInitialized()) { this.selectionData = selectionData; }//if// }//setSelectionData()// /** * Gets the component selection state. * @return Whether the component is in the selected state. */ public Boolean getIsSelected() { verifyThread(); return (Boolean) selection.getValue(); }//getIsSelected()// /** * Sets the component default selection state. * @param isSelected Whether the component is in the selected state. */ public void setIsSelected(Boolean isSelected) { verifyThread(); if(isStateful) { selection.setDefaultValue(isSelected); }//if// }//setIsSelected()// /** * 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()// /** * Adds a link for the selection. * @param link The local linkage for the selection. */ public void addSelectionLink(LinkData link) { selectionLinkage.add(link); }//addSelectionLink()// /** * Sets the selection method called when the button is pressed (intended only for push buttons). * @param selectionMethod The method called when the button is pressed. */ public void setSelectionMethod(IMethodAssociation selectionMethod) { verifyThread(); if(!isStateful) { this.selectionMethod = selectionMethod; }//if// }//setSelectionMethod()// /** * 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 validate routine is called after a synchronize. * @param autoValidate Whether the validation code in the view controller will be called after synchronizing the state. */ public void setAutoValidate(boolean autoValidate) { this.autoValidate = autoValidate; }//setAutoValidate()// /** * Sets whether the control auto synchronizes the selected state of the button. * @param autoSynchronizeSelection Whether the button state is automatically synchronized. */ public void setAutoSynchronizeSelection(boolean autoSynchronizeSelection) { verifyThread(); if(isStateful) { this.autoSynchronizeSelection = autoSynchronizeSelection; }//if// }//setAutoSynchronizeSelection()// /** * Sets the delay for the auto synchronize selection. * @param autoSynchronizeSelectionDelay The delay in terms of milliseconds between the value of zero and ten thousand. A zero value has no delay. */ public void setAutoSynchronizeSelectionDelay(long autoSynchronizeSelectionDelay) { verifyThread(); if(autoSynchronizeSelectionDelay < 0) { autoSynchronizeSelectionDelay = 0; }//if// else if(autoSynchronizeSelectionDelay > 10000) { autoSynchronizeSelectionDelay = 10000; }//else if// this.autoSynchronizeSelectionDelay = autoSynchronizeSelectionDelay; }//setAutoSynchronizeSelectionDelay()// /** * Sets whether the client views should be disabled while a selection event is processed. * @param blockOnSelection Whether push style buttons stop user input while processing the selection event. */ public void setBlockOnSelection(boolean blockOnSelection) { verifyThread(); //Does nothing. For thick clients this is always true.// }//setBlockOnSelection()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewInitialize() */ protected void internalViewInitialize() { getSwtButton().addSelectionListener(this); text.initialize(); image.initialize(); selection.initialize(); super.internalViewInitialize(); }//internalViewInitialize()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewRelease() */ protected void internalViewRelease() { destroyImage((JefImage) imageHolder.getValue()); if(!getSwtButton().isDisposed()) { getSwtButton().removeSelectionListener(this); }//if// synchronized(this) { if(autoSynchronizeSelectionTask != null) { removeTask(autoSynchronizeSelectionTask); autoSynchronizeSelectionTask = null; }//if// }//synchronized// text.release(); image.release(); selection.release(); textHolder.release(); imageHolder.release(); super.internalViewRelease(); }//internalViewRelease()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewRefresh() */ protected void internalViewRefresh() { super.internalViewRefresh(); internalViewRefreshSelection(); internalViewRefreshText(); internalViewRefreshImage(); }//internalViewRefresh()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewSynchronize() */ protected void internalViewSynchronize() { super.internalViewSynchronize(); if(isStateful) { if(!autoSynchronizeSelection) { boolean isSelected = getSwtButton().getSelection(); if(isRadio && isSelected) { //Only positive values are sent for radio buttons.// selection.setValue(selectionData != null ? selectionData : Boolean.TRUE); }//if// else if(!isRadio) { selection.setValue(isSelected ? Boolean.TRUE : Boolean.FALSE); }//else if// }//if// else { synchronized(this) { if(autoSynchronizeSelectionTask != null) { removeTask(autoSynchronizeSelectionTask); autoSynchronizeSelectionTask.execute(); }//if// }//synchronized// }//else// }//if// }//internalViewSynchronize()// /** * Refreshes the button selection. */ protected void internalViewRefreshSelection() { if((isStateful) && (selection.refresh())) { Object value = selection.getValue(); Boolean isSelected; if(value instanceof Boolean) { isSelected = (Boolean) value; }//if// else if(selectionData != null) { isSelected = Comparator.equals(selectionData, value) ? Boolean.TRUE : Boolean.FALSE; }//else if// else { isSelected = value != null ? Boolean.TRUE : Boolean.FALSE; }//else// if(getSwtButton().getSelection() != isSelected.booleanValue()) { getSwtButton().setSelection(isSelected.booleanValue()); selectionLinkage.invoke(isSelected); }//if// }//if// }//internalViewRefreshSelection()// /** * Refreshes the button image. */ protected void internalViewRefreshImage() { if(image.refresh()) { imageHolder.setValue(image.getValue()); }//if// }//internalViewRefreshImage()// /** * Refreshes the button text. */ protected void internalViewRefreshText() { if(text.refresh()) { textHolder.setValue(text.getValue()); }//if// }//internalViewRefreshText()// /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { //TODO: When is this called?// Debug.log("Default Selected Called "); }//widgetDefaultSelected()// /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetSelected(SelectionEvent e) { selectionChanged(getSwtButton().getSelection()); }//widgetSelected()// /** * Called when the selection is changed in the view control. * @param isSelected Whether the control is selected (stateful controls only). */ protected void selectionChanged(final boolean isSelected) { try { if((selectionMethod != null) || (autoSynchronizeSelection)) { if((selectionMethod == null) && (autoSynchronizeSelectionDelay > 0)) { //Ignore not-selected radio buttons - only selections count in radio buttons.// if(!isRadio || isSelected) { //Start a task to send the text to the server after a short delay. This will reduce the overhead of auto synchronizing the selection.// synchronized(this) { if(autoSynchronizeSelectionTask != null) { removeTask(autoSynchronizeSelectionTask); }//if// autoSynchronizeSelectionTask = new Task() { public void execute() { //Make sure that this task is still valid and is not being superceeded.// synchronized(Button.this) { if(autoSynchronizeSelectionTask == this) { //Cleanup after the task.// autoSynchronizeSelectionTask = null; getEventLoop().executeAsync(new IRunnable() { public Object run() { if(isRadio) { //Only positive values are sent for radio buttons.// selection.setValue(selectionData != null ? selectionData : Boolean.TRUE); }//if// else { selection.setValue(isSelected ? Boolean.TRUE : Boolean.FALSE); }//else// if(autoValidate) { postSynchronizeValidate(); }//if// return null; }//run()// }); }//if// }//synchronized// }//run()// }; addTask(autoSynchronizeSelectionTask, autoSynchronizeSelectionDelay); }//synchronized// }//if// }//if// else { if(selectionMethod != null) { selectionMethod.invoke(null, true); }//if// else { if(isRadio && isSelected) { //Only positive values are sent for radio buttons.// selection.setValue(selectionData != null ? selectionData : Boolean.TRUE); }//if// else if(!isRadio) { selection.setValue(isSelected ? Boolean.TRUE : Boolean.FALSE); }//else if// }//else// if(autoValidate) { postSynchronizeValidate(); }//if// }//else// }//if// selectionLinkage.invoke(isStateful ? isSelected ? Boolean.TRUE : Boolean.FALSE : null); }//try// catch(Throwable e) { Debug.log(e); }//catch// }//selectionChanged()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalResourceHolderChanged(com.foundation.view.swt.ResourceHolder, java.lang.Object, java.lang.Object) */ protected void internalResourceHolderChanged(ResourceHolder resource, Object oldValue, Object newValue, int flags) { if(resource == textHolder) { getSwtButton().setText((String) newValue); resize(); }//if// else if(resource == imageHolder) { destroyImage(getSwtButton().getImage()); getSwtButton().setImage(createImage((JefImage) image.getValue())); resize(); }//else if// else { super.internalResourceHolderChanged(resource, oldValue, newValue, flags); }//else// }//internalOnAssociationChanged()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalOnValueChanged(com.foundation.view.SingleResourceAssociation) */ protected void internalOnValueChanged(SingleResourceAssociation resourceAssociation, int flags) { if(resourceAssociation == text) { internalViewRefreshText(); }//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.view.swt.AbstractComponent#internalOnLinkInvoked(int, java.lang.Object) */ protected void internalOnLinkInvoked(int linkTarget, Object data) { switch(linkTarget) { case LINK_TARGET_SELECTION: { boolean isSelected = data instanceof Boolean ? ((Boolean) data).booleanValue() : false; if((isStateful) && (isSelected != getSwtButton().getSelection())) { getSwtButton().setSelection(isSelected); selectionChanged(isSelected); }//if// else { selectionChanged(true); }//else// break; }//case// case LINK_TARGET_IMAGE: { if(getSwtButton().getImage() != null) { getSwtButton().getImage().dispose(); }//if// getSwtButton().setImage(data instanceof JefImage ? SwtUtilities.getImage(getSwtButton().getDisplay(), (JefImage) data) : null); resize(); break; }//case// case LINK_TARGET_TEXT: { getSwtButton().setText(data instanceof String ? (String) data : ""); resize(); break; }//case// default: { super.internalOnLinkInvoked(linkTarget, data); break; }//default// }//switch// }//internalOnLinkInvoked()// }//Button//