/*
* Copyright 2010 Daniel Kurka
*
* 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 com.googlecode.mgwt.ui.client.widget.input.radio;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.LabelElement;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.editor.client.adapters.TakesValueEditor;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.HasName;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.HasWordWrap;
import com.googlecode.mgwt.dom.client.event.tap.Tap;
import com.googlecode.mgwt.dom.client.event.touch.TouchHandler;
import com.googlecode.mgwt.ui.client.widget.touch.TouchWidget;
/**
* A touch enabled radio button implementation
*
* @author Daniel Kurka
*/
public class MRadioButton extends TouchWidget implements HasText, HasEnabled,
HasValueChangeHandlers<Boolean>, HasName, HasValue<Boolean>, HasWordWrap,
IsEditor<LeafValueEditor<Boolean>> {
@UiField
public InputElement inputRadio;
@UiField
public LabelElement labelElement;
private LeafValueEditor<Boolean> editor;
private MRadioButtonAppearance appearance;
private final static MRadioButtonAppearance DEFAULT_APPEARANCE = GWT
.create(MRadioButtonAppearance.class);
private boolean ignoreChangeEvent;
/**
* Construct a radio button
*
* @param name the name of the group
*/
@UiConstructor
public MRadioButton(String name) {
this(DEFAULT_APPEARANCE, name);
}
/**
* Construct a radio button
*
* @param appearance the apperance to use
* @param name the group name to use
*/
public MRadioButton(MRadioButtonAppearance appearance, String name) {
this.appearance = appearance;
setElement(appearance.uiBinder().createAndBindUi(this));
inputRadio.setName(name);
sinkEvents(Event.ONCHANGE);
addTouchHandler(new TouchHandler() {
private boolean ignore;
private boolean labelOrContainer;
private int start_x;
private int start_y;
private int last_x;
private int last_y;
@Override
public void onTouchCancel(TouchCancelEvent event) {
if (ignore) {
return;
}
}
@Override
public void onTouchEnd(TouchEndEvent event) {
if (!isEnabled()) {
return;
}
if (ignore) {
return;
}
if (Math.abs(last_x - start_x) < Tap.RADIUS && Math.abs(last_y - start_y) < Tap.RADIUS) {
if (labelOrContainer) {
inputRadio.setChecked(true);
setValue(true, true);
}
}
}
@Override
public void onTouchMove(TouchMoveEvent event) {
if (!isEnabled())
return;
if (ignore)
return;
Touch touch = event.getTouches().get(0);
last_x = touch.getPageX();
last_y = touch.getPageY();
}
@Override
public void onTouchStart(TouchStartEvent event) {
if (!isEnabled())
return;
ignore = inputRadio.isChecked();
if (ignore)
return;
Touch touch = event.getTouches().get(0);
start_x = touch.getPageX();
start_y = touch.getPageY();
last_x = start_x;
last_y = start_y;
EventTarget eventTarget = event.getNativeEvent().getEventTarget();
labelOrContainer = true;
if (com.google.gwt.dom.client.Element.is(eventTarget)) {
com.google.gwt.dom.client.Element el = com.google.gwt.dom.client.Element.as(eventTarget);
if (inputRadio.isOrHasChild(el)) {
labelOrContainer = false;
}
}
}
});
addHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
if (ignoreChangeEvent) {
ignoreChangeEvent = false;
return;
}
setValue(true, true);
}
}, ChangeEvent.getType());
}
@Override
public String getText() {
return labelElement.getInnerText();
}
@Override
public void setText(String text) {
labelElement.setInnerText(text);
}
@Override
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Boolean> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
@Override
public LeafValueEditor<Boolean> asEditor() {
if (editor == null) {
editor = TakesValueEditor.of(this);
}
return editor;
}
@Override
public boolean getWordWrap() {
return !getElement().getStyle().getProperty("whiteSpace").equals("nowrap");
}
@Override
public void setWordWrap(boolean wrap) {
getElement().getStyle().setProperty("whiteSpace", wrap ? "normal" : "nowrap");
}
@Override
public Boolean getValue() {
if (isAttached()) {
return inputRadio.isChecked();
} else {
return inputRadio.isDefaultChecked();
}
}
@Override
public void setValue(Boolean value) {
setValue(value, false);
}
@Override
public void setValue(Boolean value, boolean fireEvents) {
if (value == null) {
value = false;
}
if (!fireEvents && value) {
ignoreChangeEvent = true;
}
inputRadio.setChecked(value);
inputRadio.setDefaultChecked(value);
if (fireEvents) {
ValueChangeEvent.fire(this, value);
}
}
@Override
public void setName(String name) {
replaceInputElement(DOM.createInputRadio(name));
}
private void replaceInputElement(Element elem) {
InputElement newInputElem = InputElement.as(elem);
// Collect information we need to set
boolean checked = getValue();
boolean enabled = isEnabled();
String formValue = getFormValue();
String uid = inputRadio.getId();
String accessKey = inputRadio.getAccessKey();
int sunkEvents = Event.getEventsSunk(inputRadio);
// Clear out the old input element
DOM.setEventListener(inputRadio, null);
getElement().replaceChild(newInputElem, inputRadio);
// Sink events on the new element
Event.sinkEvents(elem, Event.getEventsSunk(inputRadio));
Event.sinkEvents(inputRadio, 0);
inputRadio = newInputElem;
// Setup the new element
Event.sinkEvents(inputRadio, sunkEvents);
inputRadio.setId(uid);
if (!"".equals(accessKey)) {
inputRadio.setAccessKey(accessKey);
}
setValue(checked);
setEnabled(enabled);
setFormValue(formValue);
// Set the event listener
if (isAttached()) {
DOM.setEventListener(inputRadio, this);
}
}
/**
* set the formvalue of this radio button
*
* @param formValue the formvalue that would be sent to a server
*/
public void setFormValue(String formValue) {
inputRadio.setAttribute("value", formValue);
}
@Override
public String getName() {
return inputRadio.getName();
}
@Override
public boolean isEnabled() {
return !inputRadio.isDisabled();
}
@Override
public void setEnabled(boolean enabled) {
inputRadio.setDisabled(!enabled);
if (enabled) {
removeStyleDependentName(appearance.css().disabled());
} else {
addStyleDependentName(appearance.css().disabled());
}
}
/**
* get the form value of the input element
*
* @return the form value
*/
public String getFormValue() {
return inputRadio.getValue();
}
@UiFactory
public MRadioButtonAppearance getAppearance() {
return appearance;
}
}