/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.gef.mvc.fx.ui.properties; import java.text.MessageFormat; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.AbstractOperation; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.gef.mvc.fx.operations.ITransactionalOperation; import org.eclipse.gef.mvc.fx.ui.Messages; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.properties.IPropertySource2; /** * An {@link ITransactionalOperation} used to set or reset the value of a * property. * * @author pshah * @author anyssen * */ public class SetPropertyValueOperation extends AbstractOperation implements ITransactionalOperation { /** * Value constant to indicate that the property is to be reset to its * default value during execute/redo and undo. */ protected static final Object DEFAULT_VALUE = new Object(); private static IPropertyDescriptor getPropertyDescriptor( IPropertySource propertySource, Object propertyId) { for (IPropertyDescriptor propertyDescriptor : propertySource .getPropertyDescriptors()) { if (propertyDescriptor.getId().equals(propertyId)) { return propertyDescriptor; } } return null; } private static String getValueLabel(IPropertySource propertySource, Object propertyId, Object newValue) { IPropertyDescriptor propertyDescriptor = getPropertyDescriptor( propertySource, propertyId); return propertyDescriptor.getLabelProvider().getText(newValue); } /** content-relevant-property */ private boolean isContentRelevant = true; /** the value to set for the property */ private Object newValue; /** the old value of the property prior to executing this command */ private Object oldValue; /** the id of the property whose value has to be set */ private Object propertyId; /** the property source whose property has to be set */ private IPropertySource propertySource; /** * Constructs a new {@link SetPropertyValueOperation}. * * @param propertyLabel * A label to identify the property whose value is set by this * command. * @param propertySource * The property source which provides the property, whose value * is to be set. * @param propertyId * The id of the property whose value is to be set. * @param newValue * The new value to set for the property or * {@link #DEFAULT_VALUE} to indicate that the property should be * reset. */ public SetPropertyValueOperation(String propertyLabel, IPropertySource propertySource, Object propertyId, Object newValue) { super(MessageFormat .format(Messages.SetPropertyValueCommand_Label, new Object[] { propertyLabel, getValueLabel( propertySource, propertyId, newValue) }) .trim()); this.propertySource = propertySource; this.propertyId = propertyId; this.newValue = newValue; } @Override public boolean canExecute() { if (propertySource == null || propertyId == null) { return false; } if (newValue == DEFAULT_VALUE) { // we may only reset a property to its default value if it supports // the notion of a default value and it does not already have this // value boolean canExecute = propertySource.isPropertySet(propertyId); if (propertySource instanceof IPropertySource2) { canExecute &= (((IPropertySource2) propertySource) .isPropertyResettable(propertyId)); } return canExecute; } return true; } @Override public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { /* * Fix for bug #54250 IPropertySource.isPropertySet(String) returns * false both when there is no default value, and when there is a * default value and the property is set to that value. To correctly * determine if a reset should be done during undo, we compare the * return value of isPropertySet(String) before and after * setPropertyValue(...) is invoked. If they are different (it must have * been false before and true after -- it cannot be the other way * around), then that means we need to reset. */ boolean wasPropertySet = propertySource.isPropertySet(propertyId); oldValue = unwrapValue(propertySource.getPropertyValue(propertyId)); // set value of property to new value or reset the value, if specified if (newValue == DEFAULT_VALUE) { propertySource.resetPropertyValue(propertyId); } else { propertySource.setPropertyValue(propertyId, unwrapValue(newValue)); } // check if property was set to its default value before (so it will // have to be resetted during undo); note that if the new value is // DEFAULT_VALUE the old value may not have been the default value as // well, as the command would not be executable in this case. if (propertySource instanceof IPropertySource2) { if (!wasPropertySet && ((IPropertySource2) propertySource) .isPropertyResettable(propertyId)) { oldValue = DEFAULT_VALUE; } } else { if (!wasPropertySet && propertySource.isPropertySet(propertyId)) { oldValue = DEFAULT_VALUE; } } // TODO: infer a proper status return Status.OK_STATUS; } /** * Returns the new value to be set for the property when executing or * redoing. * * @return the new value or {@link #DEFAULT_VALUE} to indicate that the * default value should be set as the new value. */ public Object getNewValue() { return newValue; } /** * After the command has been executed or redone, returns the old value of * the property or {@link #DEFAULT_VALUE} if the property did not have a * value before. * * @return the old value of the property or {@link #DEFAULT_VALUE}. */ public Object getOldValue() { return oldValue; } /** * Returns the id by which to identify the property whose value is to be * set. * * @return the id of the property whose value is to be set. */ public Object getPropertyId() { return propertyId; } /** * Returns the {@link IPropertySource} which provides the property, whose * value is to be set. * * @return the {@link IPropertySource} which provides the property. */ public IPropertySource getPropertySource() { return propertySource; } @Override public boolean isContentRelevant() { return isContentRelevant; } @Override public boolean isNoOp() { return oldValue == newValue || (oldValue != null && oldValue.equals(newValue)); } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } /** * Sets the content-relevancy of this operation to the given value. * * @param isContentRelevant * <code>true</code> if this operation is content-relevant, * <code>false</code> otherwise. */ public void setContentRelevant(boolean isContentRelevant) { this.isContentRelevant = isContentRelevant; } @Override public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { if (oldValue == DEFAULT_VALUE) { propertySource.resetPropertyValue(propertyId); } else { propertySource.setPropertyValue(propertyId, oldValue); } return Status.OK_STATUS; } private Object unwrapValue(Object value) { if (value instanceof IPropertySource) { return ((IPropertySource) value).getEditableValue(); } return value; } }