// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.client.widgets.properties;
import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.TextBoxBase;
/**
* Property editor base for text box editors.
*
*/
public class TextPropertyEditorBase extends PropertyEditor {
/**
* Thrown to indicate that text input is invalid.
*/
public static class InvalidTextException extends Exception {
public InvalidTextException(String message) {
super(message);
}
}
// This is the GWT object that supports the property editor
// It can be TextBoxBase object, currently in App Inventor only
// TextBox and TextArea
protected TextBoxBase textEdit;
private boolean hasFocus;
/**
* Creates a new instance of the property editor.
*/
public TextPropertyEditorBase(final TextBoxBase widget) {
textEdit = widget;
textEdit.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
handleKeyPress(event.getCharCode());
}
});
textEdit.addKeyUpHandler(new KeyUpHandler() {
@Override
public void onKeyUp(KeyUpEvent event) {
handleKeyUp(event.getNativeKeyCode());
}
});
textEdit.addValueChangeHandler(new ValueChangeHandler() {
@Override
public void onValueChange(ValueChangeEvent event) {
validateText();
}
});
// NOTE(lizlooney) - The following handlers for focus, blur, and click are needed to workaround
// a bug with WebKit browsers (chrome and safari) where clicking in the TextBox causes it to
// gain focus, but then immediately lose focus (blur). To work around the problem, we keep
// track of whether the TextBox has focus using a FocusHandler and a BlurHandler. Then, we use
// a ClickHandler and if we get a ClickEvent and the TextBox does not have focus, we explicitly
// call setFocus.
textEdit.addFocusHandler(new FocusHandler() {
@Override
public void onFocus(FocusEvent event) {
hasFocus = true;
}
});
textEdit.addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
hasFocus = false;
// Calling validateText here means that we will save the changed property value (if it is
// valid) when this property editor loses focus (for example, when the user clicks on
// another property editor).
validateText();
}
});
textEdit.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (!hasFocus) {
textEdit.setFocus(true);
}
}
});
initWidget(textEdit); //kludge for now fix this with instanceOf?
setHeight("2em");
}
@Override
protected void onUnload() {
// onUnload is called immediately before a widget becomes detached from the browser's document.
// Calling validateText here means that we will save the changed property value (if it is
// valid) when the user clicks on another component.
validateText();
super.onUnload();
}
@Override
protected void updateValue() {
textEdit.setText(property.getValue());
}
private void handleKeyPress(char keyCode) {
if (keyCode == KeyCodes.KEY_ENTER || keyCode == KeyCodes.KEY_TAB) {
// Pressing <tab>, <enter> or <return> will surrender focus.
textEdit.cancelKey();
textEdit.setFocus(false);
} else if (!validateKeyCode(keyCode)) {
textEdit.cancelKey();
}
}
private void handleKeyUp(int keyCode) {
if (keyCode == KeyCodes.KEY_ESCAPE) {
// Pressing <esc> will reset the content of the editor to the previous property value as well
// as surrender focus.
updateValue(); // Restore previous property value.
textEdit.cancelKey();
textEdit.setFocus(false);
}
}
/*
* Validates the text in the textEdit and if it is valid, sets the property
* value to the text.
*/
private void validateText() {
String text = textEdit.getText();
try {
validate(text);
property.setValue(text);
} catch (InvalidTextException e) {
String error = e.getMessage();
if (error == null || error.isEmpty()) {
error = MESSAGES.malformedInputError();
}
Window.alert(error);
updateValue(); // Restore previous property value.
}
}
/**
* Validates the given key code.
*
* <p/>The implementation here does no validation. Subclasses may override
* this method to provide actual validation.
*
* @param keyCode key code to validate
* @return true if the keycode is allowed, false if it is not allowed.
*/
protected boolean validateKeyCode(char keyCode) {
return true;
}
/**
* Validates the given text. Throw an InvalidTextException if the text is
* invalid.
*
* <p/>The implementation here does no validation. Subclasses may override
* this method to provide actual validation.
*
* @param text input string to validate
* @throws InvalidTextException if the text is invalid
*/
protected void validate(String text) throws InvalidTextException {
}
}