/*******************************************************************************
* Copyright 2013 Geoscience Australia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package au.gov.ga.earthsci.editable.renderers;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.*;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.Value;
import org.eclipse.sapphire.ValueProperty;
import org.eclipse.sapphire.modeling.annotations.NumericRange;
import org.eclipse.sapphire.modeling.util.MiscUtil;
import org.eclipse.sapphire.ui.assist.internal.PropertyEditorAssistDecorator;
import org.eclipse.sapphire.ui.forms.FormComponentPart;
import org.eclipse.sapphire.ui.forms.PropertyEditorPart;
import org.eclipse.sapphire.ui.forms.swt.PropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.PropertyEditorPresentationFactory;
import org.eclipse.sapphire.ui.forms.swt.SwtPresentation;
import org.eclipse.sapphire.ui.forms.swt.ValuePropertyEditorPresentation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Text;
import au.gov.ga.earthsci.editable.annotations.Accuracy;
import au.gov.ga.earthsci.editable.annotations.Sync;
/**
* Property editor for double values that shows a scale bar.
* <p/>
* Adapted from Sapphire's {@link ScalePropertyEditorRenderer}.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class ScaleDoublePropertyEditorRenderer extends ValuePropertyEditorPresentation
{
private double minimum;
private double maximum;
private double scale;
private boolean sync = false;
private int accuracy = 3;
private Scale widget;
private Text textField;
public ScaleDoublePropertyEditorRenderer(FormComponentPart part, SwtPresentation parent, Composite composite)
{
super(part, parent, composite);
}
@Override
protected void createContents(final Composite parent)
{
final PropertyEditorPart part = part();
final ValueProperty property = (ValueProperty) part.property().definition();
final NumericRange rangeAnnotation = property.getAnnotation(NumericRange.class);
this.sync = property.getAnnotation(Sync.class) != null;
final Accuracy accuracyAnnotation = property.getAnnotation(Accuracy.class);
if (accuracyAnnotation != null)
{
accuracy = accuracyAnnotation.value();
}
try
{
this.minimum = Double.parseDouble(rangeAnnotation.min());
this.maximum = Double.parseDouble(rangeAnnotation.max());
}
catch (NumberFormatException e)
{
// Should not happen here. We already checked this in property editor applicability test.
throw new RuntimeException(e);
}
//calculate a scale such that the range of the scale widget is between 200 and 2000 if accuracy == 3
double range = this.maximum - this.minimum;
int log10i = (int) Math.round(Math.floor(Math.log10(range)));
scale = Math.pow(10, accuracy - log10i - 1);
double accuracyMinimum = Math.pow(10, accuracy) * 2;
if (range * scale < accuracyMinimum)
{
scale *= 10;
}
int widgetMinimum = 0;
int widgetMaximum = (int) (range * scale); //guaranteed between 200 and 2000
final Composite composite = createMainComposite(parent);
composite.setLayout(glspacing(glayout(2, 0, 0), 2));
final Composite textFieldComposite = new Composite(composite, SWT.NONE);
textFieldComposite.setLayoutData(gdwhint(gd(), 60));
textFieldComposite.setLayout(glspacing(glayout(2, 0, 0), 2));
final PropertyEditorAssistDecorator decorator = createDecorator(textFieldComposite);
decorator.control().setLayoutData(gdvalign(gd(), SWT.TOP));
this.textField = new Text(textFieldComposite, SWT.BORDER);
this.textField.setLayoutData(gdhfill());
this.textField.addModifyListener(new ModifyListener()
{
@Override
public void modifyText(final ModifyEvent event)
{
setPropertyValue(textField.getText(), !sync);
}
});
/*final TextOverlayPainter.Controller textOverlayPainterController = new TextOverlayPainter.Controller()
{
@Override
public String overlay()
{
return property().getDefaultText();
}
};
TextOverlayPainter.install(this.textField, textOverlayPainterController);*/
this.widget = new Scale(composite, SWT.HORIZONTAL);
this.widget.setLayoutData(gdhfill());
this.widget.setMinimum(widgetMinimum);
this.widget.setMaximum(widgetMaximum);
this.widget.setIncrement(1);
this.widget.setPageIncrement(10);
this.widget.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(final SelectionEvent event)
{
final int i = widget.getSelection();
final double d = i / scale + minimum;
final double r = Math.round(d * scale) / (scale); //get rid of double rounding errors
setPropertyValue(String.valueOf(r), !sync);
}
});
decorator.addEditorControl(composite);
addControl(this.textField);
addControl(this.widget);
}
@Override
protected void handlePropertyChangedEvent()
{
super.handlePropertyChangedEvent();
final Value<?> value = property();
final String existingValueInTextField = this.textField.getText();
final String newValueForTextField = value.text(false);
if (!existingValueInTextField.equals(newValueForTextField))
{
this.textField.setText(newValueForTextField == null ? MiscUtil.EMPTY_STRING : newValueForTextField);
}
final Double newValueDouble = (Double) value.content(true);
int newValueForScale =
(newValueDouble == null ? this.widget.getMinimum()
: (int) Math.round((newValueDouble.doubleValue() - minimum) * scale));
if (this.widget.getSelection() != newValueForScale)
{
this.widget.setSelection(newValueForScale);
}
}
@Override
protected void handleFocusReceivedEvent()
{
this.widget.setFocus();
}
public static class Factory extends PropertyEditorPresentationFactory
{
public boolean isApplicableTo(final PropertyEditorPart propertyEditorDefinition)
{
final PropertyDef property = propertyEditorDefinition.property().definition();
if (property.isOfType(Double.class))
{
final NumericRange rangeAnnotation = property.getAnnotation(NumericRange.class);
if (rangeAnnotation != null)
{
final String minStr = rangeAnnotation.min();
final String maxStr = rangeAnnotation.max();
if (minStr.length() > 0 && maxStr.length() > 0)
{
return true;
}
}
}
return false;
}
@Override
public PropertyEditorPresentation create(PropertyEditorPart part, SwtPresentation parent, Composite composite)
{
if (isApplicableTo(part))
{
return new ScaleDoublePropertyEditorRenderer(part, parent, composite);
}
return null;
}
}
}