/*
* Copyright 2015 Max Schuster.
*
* 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 eu.maxschuster.vaadin.signaturefield;
import com.vaadin.annotations.JavaScript;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.server.AbstractJavaScriptExtension;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.Component;
import com.vaadin.ui.JavaScriptFunction;
import com.vaadin.util.ReflectTools;
import elemental.json.JsonArray;
import elemental.json.JsonString;
import elemental.json.JsonValue;
import eu.maxschuster.vaadin.signaturefield.shared.MimeType;
import eu.maxschuster.vaadin.signaturefield.shared.SignatureFieldExtensionState;
import java.io.Serializable;
import java.lang.reflect.Method;
/**
* A javascript extension that extends a {@link SignatureField} with the
* client-side logic.
*
* <a href="https://github.com/szimek/signature_pad">signature_pad</a>
* by Szymon Nowak (<a href="https://github.com/szimek">szimek</a>) is used to
* capture the signature at the client-side.
* @author Max Schuster
* @see <a href="https://github.com/szimek/signature_pad">signature_pad</a>
*/
@JavaScript("vaadin://addons/signaturefield/dist/SignatureFieldExtension.min.js")
@StyleSheet("vaadin://addons/signaturefield/dist/SignatureFieldExtension.css")
public class SignatureFieldExtension extends AbstractJavaScriptExtension {
private static final long serialVersionUID = 1L;
/**
* Current signature value
*/
private String signature;
/**
* Listener that gets called when the signature changes
*/
public interface SignatureChangeListener extends Serializable {
public static final Method METHOD = ReflectTools.findMethod(
SignatureChangeListener.class, "signatureChange",
SignatureChangeEvent.class);
public void signatureChange(SignatureChangeEvent event);
}
/**
* A signature change event
*/
public static class SignatureChangeEvent extends Component.Event {
private static final long serialVersionUID = 1L;
private final SignatureFieldExtension extension;
private final String signature;
public SignatureChangeEvent(Component source,
SignatureFieldExtension extension, String signature) {
super(source);
this.extension = extension;
this.signature = signature;
}
/**
* @return The extension that has fired this event
*/
public SignatureFieldExtension getExtension() {
return extension;
}
/**
* @return The new signature value
*/
public String getSignature() {
return signature;
}
}
public SignatureFieldExtension(SignatureField target) {
// extend the target
super(target);
/*
* Gets called from the client-side when it wants to change the signatue
* value at the server-side.
*/
addFunction("fireSignatureChange", new JavaScriptFunction() {
@Override
public void call(JsonArray arguments) {
String signature;
JsonValue jsonValue = arguments.get(0);
if (jsonValue instanceof JsonString) {
signature = jsonValue.asString();
} else {
signature = null;
}
setSignature(signature, true);
fireSignatureChangeEvent(signature);
}
});
}
@Override
protected SignatureFieldExtensionState getState() {
return (SignatureFieldExtensionState) super.getState();
}
@Override
protected SignatureFieldExtensionState getState(boolean markAsDirty) {
return (SignatureFieldExtensionState) super.getState(markAsDirty);
}
@Override
public void beforeClientResponse(boolean initial) {
super.beforeClientResponse(initial);
if (initial) {
updateSignature();
}
}
/**
* Updates the client-side with the current signature value
*/
protected void updateSignature() {
callFunction("setSignature", getSignature());
}
/**
* Clears the field on the client-side
*/
public void clear() {
callFunction("clear");
}
/**
* @return Current signature value
*/
public String getSignature() {
return signature;
}
/**
* Set signature current signature value.
* @param signature Signature
* @param repaintIsNotNeeded Repaint is not needed
*/
public void setSignature(String signature, boolean repaintIsNotNeeded) {
String oldSignature = this.signature;
if (!SharedUtil.equals(oldSignature, signature)) {
this.signature = signature;
if (!repaintIsNotNeeded) {
updateSignature();
}
}
}
/**
* Set signature current signature value.
* @param signature Signature
*/
public void setSignature(String signature) {
setSignature(signature, false);
}
/**
* Fires a new {@link SignatureChangeEvent} with the given Signature.
* @param signature New signature
*/
public void fireSignatureChangeEvent(String signature) {
fireEvent(new SignatureChangeEvent((Component) getParent(), this, signature));
}
/**
* Adds a {@link SignatureChangeListener}.
* @param listener Listener to add
*/
public void addSignatureChangeListener(SignatureChangeListener listener) {
addListener(SignatureChangeEvent.class, listener, SignatureChangeListener.METHOD);
}
/**
* Removes a {@link SignatureChangeListener}.
* @param listener Listener to remove
*/
public void removeSignatureChangeListener(SignatureChangeListener listener) {
removeListener(SignatureChangeEvent.class, listener, SignatureChangeListener.METHOD);
}
/**
* Returns true if the extension is immediate.
* @return Extension is immediate.
*/
public boolean getImmediate() {
return getState(false).immediate;
}
/**
* Sets the immediate value of this extension.
* @param immediate New immediate value of this extension.
*/
public void setImmediate(boolean immediate) {
getState().immediate = immediate;
}
/**
* Returns true if extension is read only.
* @return Extension is read only.
*/
public boolean getReadOnly() {
return getState(false).readOnly;
}
/**
* Sets the read only value of this extension.
* @param readOnly Extension is read only.
*/
public void setReadOnly(boolean readOnly) {
getState().readOnly = readOnly;
}
/**
* Gets the radius of a single dot.
*
* @return Radius of a single dot.
*/
public Double getDotSize() {
return getState(false).dotSize;
}
/**
* Sets the radius of a single dot.
*
* @param dotSize Radius of a single dot.
*/
public void setDotSize(Double dotSize) {
getState().dotSize = dotSize;
}
/**
* Gets the minimum width of a line. Defaults to 0.5.
*
* @return Minimum width of a line.
*/
public double getMinWidth() {
return getState(false).minWidth;
}
/**
* Sets the minimum width of a line. Defaults to 0.5.
*
* @param minWidth Minimum width of a line.
*/
public void setMinWidth(double minWidth) {
getState().minWidth = minWidth;
}
/**
* Gets the maximum width of a line.
*
* @return Maximum width of a line.
*/
public double getMaxWidth() {
return getState(false).maxWidth;
}
/**
* Sets the maximum width of a line.
*
* @param maxWidth Maximum width of a line.
*/
public void setMaxWidth(double maxWidth) {
getState().maxWidth = maxWidth;
}
/**
* Gets the color used to clear the background. Can be any color format
* accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent
* black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque
* white) if you'd like to save signatures as JPEG images.<br>
* <br>
* Some predefined colors can be found in class {@link SampleColors}
*
* @return Color used to clear the background.
*/
public String getBackgroundColor() {
return getState(false).backgroundColor;
}
/**
* Sets the color used to clear the background. Can be any color format
* accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent
* black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque
* white) if you'd like to save signatures as JPEG images.<br>
* <br>
* Some predefined colors can be found in class {@link SampleColors}
*
* @param backgroundColor Color used to clear the background.
*/
public void setBackgroundColor(String backgroundColor) {
getState().backgroundColor = backgroundColor;
}
/**
* Sets the color used to draw the lines. Can be any color format accepted
* by context.fillStyle.<br>
* <br>
* Some predefined colors can be found in class {@link SampleColors}
*
* @return The color used to draw the lines.
*/
public String getPenColor() {
return getState(false).penColor;
}
/**
* Sets the color used to draw the lines. Can be any color format accepted
* by context.fillStyle.<br>
* <br>
* Some predefined colors can be found in class {@link SampleColors}
*
* @param penColor The color used to draw the lines.
*/
public void setPenColor(String penColor) {
getState().penColor = penColor;
}
/**
* Gets the velocity filter weight
*
* @return The velocity filter weight
*/
public double getVelocityFilterWeight() {
return getState(false).velocityFilterWeight;
}
/**
* Sets the velocity filter weight
*
* @param velocityFilterWeight The velocity filter weight
*/
public void setVelocityFilterWeight(double velocityFilterWeight) {
getState().velocityFilterWeight = velocityFilterWeight;
}
/**
* Sets the {@link MimeType} of generated images
*
* @return The {@link MimeType} of generated images
*/
public MimeType getMimeType() {
String mimeType = getState(false).mimeType;
if (mimeType != null) {
return MimeType.valueOfMimeType(mimeType);
} else {
return null;
}
}
/**
* Sets the {@link MimeType} of generated images
*
* @param mimeType The {@link MimeType} of generated images
*/
public void setMimeType(MimeType mimeType) {
if (mimeType != null) {
getState().mimeType = mimeType.getMimeType();
} else {
getState().mimeType = null;
}
}
/**
* Gets the visibility of the clear button
*
* @return Should show a clear button in the {@link SignatureFieldExtension}
*/
public boolean isClearButtonEnabled() {
return getState(false).clearButtonEnabled;
}
/**
* Sets the visibility of the clear button
*
* @param clearButtonEnabled Should show a clear button in the
* {@link SignatureFieldExtension}
*/
public void setClearButtonEnabled(boolean clearButtonEnabled) {
getState().clearButtonEnabled = clearButtonEnabled;
}
}