/******************************************************************************* * Copyright � 2009 Florian Pirchner 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: * Florian Pirchner � initial API and implementation (based on other ridgets of * compeople AG) * compeople AG - adjustments for Riena v1.2 *******************************************************************************/ package org.eclipse.riena.internal.ui.ridgets.swt; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import org.eclipse.core.databinding.beans.BeansObservables; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.runtime.IStatus; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.widgets.Control; import org.eclipse.riena.ui.ridgets.AbstractMarkerSupport; import org.eclipse.riena.ui.ridgets.IActionListener; import org.eclipse.riena.ui.ridgets.IRidget; import org.eclipse.riena.ui.ridgets.ITraverseRidget; import org.eclipse.riena.ui.ridgets.swt.AbstractEditableRidget; import org.eclipse.riena.ui.ridgets.swt.BasicMarkerSupport; /** * This ridget is an abstraction for all TraverseRidgets and offers general * functionality. * <p> * Additionally it adds a pattern feature to the toolTipText. See * {@link ITraverseRidget#VALUE_PATTERN} * <p> * <b>Value ranges and automatic value adjustments:</b> * <p> * INCREMENT * <p> * It has to be greater than zero. * <p> * Automatic adjustments: * <ul> * <li><b>increment <= 0:</b> The increment will be set to 1.</li> * <li><b>increment > maximum - minimum</b> The increment will be decreased * to maximum - minimum</li> * </ul> * <p> * MAXIMUM * <p> * It has to be equal or greater than zero. * <p> * Invalid values: * <ul> * <li><b>maximum <= 0:</b> The set operation will be ignored.</li> * <li><b>maximum <= minimum:</b> The set operation will be ignored.</li> * </ul> * Automatic adjustments: * <p> * <ul> * <li><b>maximum < value:</b> The value will be decreased to maximum.</li> * <li><b>maximum - minimum < increment:</b> See INCEMENT.</li> * <li><b>maximum - minimum < pageIncrement:</b> See PAGE INCREMENT.</li> * </ul> * <p> * MINIMUM * <p> * Invalid values: * <ul> * <li><b>minimum < 0:</b> The set operation will be ignored.</li> * <li><b>minimum >= maximum:</b> The set operation will be ignored.</li> * </ul> * Automatic adjustments: * <p> * <ul> * <li><b>minimum > value:</b> The value will be increased to minimum.</li> * <li><b>maximum - minimum < increment:</b> See INCEMENT.</li> * <li><b>maximum - minimum < pageIncrement:</b> See PAGE INCREMENT.</li> * </ul> * PAGE INCREMENT * <p> * It has to be greater than zero. * </p> * Automatic adjustments: * <p> * <ul> * <li><b>increment <= 0:</b> The increment will be set to 1.</li> * <li><b>increment > maximum - thumb - minimum</b> The increment will be * decreased to maximum - thumb - minimum.</li> * </ul> * VALUE * <p> * It has to be zero or greater and in the range of minimum and maximum. * <p> * Automatic adjustments: * <ul> * <li><b>value < 0:</b> The value will be set to minimum.</li> * <li><b>value < minimum:</b> The value will be set to minimum.</li> * <li><b>value > maximum - thumb:</b> The value will be set to maximum - * thumb.</li> * </ul> * * @since 1.2 */ public abstract class AbstractTraverseRidget extends AbstractEditableRidget implements ITraverseRidget { /** * This property is used by the databinding to sync ridget and model. It is * always fired before its sibling {@link ITraverseRidget#PROPERTY_VALUE} to * ensure that the model is updated before any listeners try accessing it. * <p> * This property is not API. Do not use in client code. */ private static final String PROPERTY_VALUE_INTERNAL = "valueInternal"; //$NON-NLS-1$ private final ActionObserver actionObserver; private final TooltipChangeHandler tooltipChangedHandler; protected boolean initialized; protected int maximum; protected int minimum; protected int increment; protected int pageIncrement; protected int value; public AbstractTraverseRidget() { initialized = false; maximum = Integer.MIN_VALUE; minimum = Integer.MIN_VALUE; increment = Integer.MIN_VALUE; pageIncrement = Integer.MIN_VALUE; value = Integer.MIN_VALUE; actionObserver = new ActionObserver(this); tooltipChangedHandler = new TooltipChangeHandler(); addListener(new ActionListener()); addPropertyChangeListener(IRidget.PROPERTY_TOOLTIP, tooltipChangedHandler); } public final void addListener(final IActionListener listener) { actionObserver.addListener(listener); } public void triggerListener() { actionObserver.fireAction(null); } @Override public void bindUIControl() { final Control control = getUIControl(); if (control != null) { initFromUIControl(); addSelectionListener(control, actionObserver); updateUIControl(); } } public int getIncrement() { return increment; } public int getMaximum() { return maximum; } public int getMinimum() { return minimum; } public int getPageIncrement() { return pageIncrement; } public int getValue() { return value; } /** * This method is not API. Do not use in client code. * * @noreference This method is not intended to be referenced by clients. */ public final int getValueInternal() { return getValue(); } /** * Always returns true because mandatory markers do not make sense for this * ridget. */ @Override public boolean isDisableMandatoryMarker() { return true; } public final void removeListener(final IActionListener listener) { actionObserver.removeListener(listener); } public synchronized boolean revalidate() { int val = 0; if (getUIControl() != null) { val = getValue(); } disableMandatoryMarkers(true); final IStatus status = checkAllRules(val, new ValidationCallback(false)); if (status.isOK()) { getValueBindingSupport().updateFromTarget(); } return !isErrorMarked(); } public void setIncrement(int increment) { increment = preSetIncrement(increment); final Object oldValue = this.increment; this.increment = increment; updateUIIncrement(); firePropertyChange(ITraverseRidget.PROPERTY_INCREMENT, oldValue, this.getIncrement()); } public void setMaximum(final int maximum) { checkMaximum(maximum); preSetMaximum(maximum); final Object oldValue = this.maximum; this.maximum = maximum; updateUIMaximum(); firePropertyChange(ITraverseRidget.PROPERTY_MAXIMUM, oldValue, this.maximum); } public void setMinimum(final int minimum) { checkMinimum(minimum); preSetMinimum(minimum); final Object oldValue = this.minimum; this.minimum = minimum; updateUIMinimum(); firePropertyChange(ITraverseRidget.PROPERTY_MINIMUM, oldValue, this.minimum); } public void setPageIncrement(int pageIncrement) { pageIncrement = preSetPageIncrement(pageIncrement); final Object oldValue = this.pageIncrement; this.pageIncrement = pageIncrement; updateUIPageIncrement(); firePropertyChange(ITraverseRidget.PROPERTY_PAGE_INCREMENT, oldValue, this.pageIncrement); } public void setValue(int value) { value = preSetValue(value); final Object oldValue = this.value; this.value = value; updateUIValue(); tooltipChangedHandler.updateTooltipPattern(); firePropertyChange(PROPERTY_VALUE_INTERNAL, oldValue, this.value); firePropertyChange(ITraverseRidget.PROPERTY_VALUE, oldValue, this.value); } /** * This method is not API. Do not use in client code. * * @noreference This method is not intended to be referenced by clients. */ public final void setValueInternal(final int value) { setValue(value); } // protected methods //////////////////// /** * Add the given selection listener to the control. * * @param control * a control instance; never null * @param listener * a selection listener; never null */ protected abstract void addSelectionListener(Control control, SelectionListener listener); /** * Validates whether the maximum is in the valid range. * <p> * * See <i>'Invalid values'</i> in java doc of {@link AbstractTraverseRidget} * section <i>MAXIMUM</i>. * * @throws IllegalArgumentException * if the given maximum is not a valid value. */ protected void checkMaximum(final int maximum) { if (maximum <= minimum) { new Message(Message.MAX_LE_MIN, maximum, minimum).push(); } if (maximum <= 0) { new Message(Message.MAX_LE_ZERO, maximum).push(); } } /** * Validates whether the minimum is in the valid range. * <p> * * See <i>'Invalid values'</i> in java doc of {@link AbstractTraverseRidget} * section <i>MINIMUM</i>. * * @throws IllegalArgumentException * if the given minimum is not a valid value. */ protected void checkMinimum(final int minimum) { if (minimum >= maximum) { new Message(Message.MIN_GE_MAX, minimum, maximum).push(); } if (minimum < 0) { new Message(Message.MIN_LT_ZERO, minimum).push(); } } @Override protected AbstractMarkerSupport createMarkerSupport() { return new BasicMarkerSupport(this, propertyChangeSupport); } /** * @return The observable value of the ridget. */ @Override protected IObservableValue getRidgetObservable() { return BeansObservables.observeValue(this, PROPERTY_VALUE_INTERNAL); } /** * Returns the value of the given uiControl which is the selection. * * @param the * uiControl * @return a integer greater equal zero as the value of the control. */ protected abstract int getValue(Control control); /** * Initializes the ridget on binding with the values of the uiControl. This * is responsible to ensure, that the properties of the ridget and the * uiControl are equal. */ protected void initFromUIControl() { if (getUIControl() != null && !initialized) { if (getMaximum() == Integer.MIN_VALUE) { setMaximum(getUIControlMaximum()); } if (getMinimum() == Integer.MIN_VALUE) { setMinimum(getUIControlMinimum()); } if (getIncrement() == Integer.MIN_VALUE) { setIncrement(getUIControlIncrement()); } if (getPageIncrement() == Integer.MIN_VALUE) { setPageIncrement(getUIControlPageIncrement()); } if (getValue() == Integer.MIN_VALUE) { setValue(getUIControlSelection()); } initAdditionalsFromUIControl(); initialized = true; } } protected abstract void initAdditionalsFromUIControl(); protected abstract int getUIControlMaximum(); protected abstract int getUIControlMinimum(); protected abstract int getUIControlIncrement(); protected abstract int getUIControlPageIncrement(); protected abstract int getUIControlSelection(); // helping methods // //////////////// /** * This method changes the values of properties of this ridget, if the given * increment requires this by java doc. As a special case, it can also * change the given increment. * <p> * See <i>'Automatic adjustments'</i> in java doc of * {@link AbstractTraverseRidget} section <i>INCREMENT</i>. * * @param increment * the increment to set * @return an adjusted increment */ protected int preSetIncrement(int increment) { if (!initialized) { return increment; } if (increment <= 0) { increment = 1; } else if (increment > maximum - minimum) { increment = maximum - minimum; } return increment; } /** * This method changes the values of properties of this ridget, if the given * maximum requires this by java doc. As a special case, it can also change * the given maximum. * <p> * See <i>'Automatic adjustments'</i> in java doc of * {@link AbstractTraverseRidget} section <i>MAXIMUM</i>. * * @param maximum * the maximum to set * @return an adjusted maximum */ protected int preSetMaximum(final int maximum) { if (!initialized) { return maximum; } if (maximum < value) { setValue(maximum); } final int deltaMaxMin = maximum - minimum; if (deltaMaxMin < increment) { setIncrement(deltaMaxMin); } if (deltaMaxMin < pageIncrement) { setPageIncrement(deltaMaxMin); } return maximum; } /** * This method changes the values of properties of this ridget, if the given * minimum requires this by java doc. As a special case, it can also change * the given minimum. * <p> * See <i>'Automatic adjustments'</i> in java doc of * {@link AbstractTraverseRidget} section <i>MINIMUM</i>. * * @param minimum * the minimum to set * @return an adjusted minimum */ protected int preSetMinimum(final int minimum) { if (!initialized) { return minimum; } if (value < minimum) { setValue(minimum); } final int deltaMaxMin = maximum - minimum; if (deltaMaxMin < increment) { setIncrement(deltaMaxMin); } if (deltaMaxMin < pageIncrement) { setPageIncrement(deltaMaxMin); } return minimum; } /** * This method changes the values of properties of this ridget, if the given * pageIncrement requires this by java doc. As a special case, it can also * change the given pageIncrement. * <p> * See <i>'Automatic adjustments'</i> in java doc of * {@link AbstractTraverseRidget} section <i>PAGE INCREMENT</i>. * * @param pageIncrement * the pageIncrement to set * @return an adjusted pageIncrement */ protected int preSetPageIncrement(int pageIncrement) { if (!initialized) { return pageIncrement; } if (pageIncrement <= 0) { pageIncrement = 1; } else if (pageIncrement > maximum - minimum) { pageIncrement = maximum - minimum; } return pageIncrement; } /** * This method changes the values of properties of this ridget, if the given * value requires this by java doc. As a special case, it can also change * the given value. * <p> * See <i>'Automatic adjustments'</i> in java doc of * {@link AbstractTraverseRidget} section <i>VALUE</i>. * * @param value * the value to set * @return an adjusted value */ protected int preSetValue(int value) { if (!initialized) { return value; } if (value < 0 || value < minimum) { value = getMinimum(); } if (value > maximum) { value = maximum; } return value; } /** * Remove the given selection listener from the control. * * @param control * a control instance; never null * @param listener * a selection listener; never null */ protected abstract void removeSelectionListener(Control control, SelectionListener listener); /** * Replaces the tooltip pattern ( <code>ITraverseRidget.VALUE_PATTERN</code> * ) with the value of this ridget. */ protected String replaceToolTipPattern(final String toolTipText) { final String strgValue = Integer.toString(getValue()); return toolTipText.replace(ITraverseRidget.VALUE_PATTERN, strgValue); } @Override protected void unbindUIControl() { super.unbindUIControl(); final Control control = getUIControl(); if (control != null) { addSelectionListener(control, actionObserver); } } /** * Updates the uiControl with the values of the ridget. */ protected void updateUIControl() { updateUIMaximum(); updateUIMinimum(); updateUIIncrement(); updateUIPageIncrement(); updateUIValue(); tooltipChangedHandler.updateTooltipPattern(); } /** * Updates the increment of the uiControl with the increment of the ridget. */ protected abstract void updateUIIncrement(); /** * Updates the maximum of the uiControl with the maximum of the ridget. */ protected abstract void updateUIMaximum(); /** * Updates the minimum of the uiControl with the minimum of the ridget. */ protected abstract void updateUIMinimum(); /** * Updates the pageIncrement of the uiControl with the pageIncrement of the * ridget. */ protected abstract void updateUIPageIncrement(); /** * Updates the value of the uiControl with the value of the ridget. */ protected abstract void updateUIValue(); // helping classes ////////////////// /** * This class listens for selection events sent by the uiControl. */ private final class ActionListener implements IActionListener { public void callback() { if (getUIControl() != null) { final int selection = getValue(getUIControl()); setValue(selection); } } } /** * A listener that listens for a changed toolTipTexts to replace the * selectionPattern. */ private final class TooltipChangeHandler implements PropertyChangeListener { private String acceptedPatternTooltip; private boolean updateMode; public void propertyChange(final PropertyChangeEvent event) { // if updateMode, the method does nothing. if (updateMode) { return; } // if the newValue == null, reset the acceptedPatternTooltip and // return. final String newValue = (String) event.getNewValue(); if (newValue == null || newValue.equals("")) { //$NON-NLS-1$ acceptedPatternTooltip = null; return; } boolean replace = true; // if the newValue contains the selectionPattern, the newValue must // be the new acceptedPatternTooltip. if (newValue.contains(ITraverseRidget.VALUE_PATTERN)) { acceptedPatternTooltip = newValue; } else if (acceptedPatternTooltip != null) { // test whether the normalized acceptedPatternTooltip matches // the newValue. If it does not, i can not be a normalized // patternTooltip, because the current selection of the // uiControl would already have been inserted in the tooltip. final String testReplaced = replaceToolTipPattern(acceptedPatternTooltip); if (!testReplaced.equals(newValue)) { acceptedPatternTooltip = null; } else { // already replaced replace = false; } } else { acceptedPatternTooltip = null; } if (replace) { updateTooltipPattern(); } } /** * Updates the tooltipText of the uiControl by replacing the selection * pattern. */ protected void updateTooltipPattern() { final Control control = getUIControl(); if (control == null) { return; } if (acceptedPatternTooltip != null) { final String toolTipText = replaceToolTipPattern(acceptedPatternTooltip); try { updateMode = true; setToolTipText(toolTipText); } finally { updateMode = false; } } } } /** * A helper which throws IllegalArgumentExceptions. */ protected static class Message { public static final String MAX_LE_MIN = "The maximum value of %d must be greater than the minimum value of %d"; //$NON-NLS-1$ public static final String MAX_LE_ZERO = "The maximum value of %d must be greater than zero"; //$NON-NLS-1$ public static final String MIN_GE_MAX = "The minimum value of %d must be lower than the maximum value of %d"; //$NON-NLS-1$ public static final String MIN_LT_ZERO = "The minimum value of %d must be greater than zero"; //$NON-NLS-1$ private final String message; protected Message(final String msgConstant, final Object... attributes) { message = String.format(msgConstant, attributes); } protected void push() { throw new IllegalArgumentException(message); } } }