// -*- 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.editor.simple.components;
import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.appinventor.client.editor.simple.SimpleEditor;
import com.google.appinventor.client.output.OdeLog;
import com.google.gwt.event.dom.client.ErrorEvent;
import com.google.gwt.event.dom.client.ErrorHandler;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DeckPanel;
import com.google.gwt.user.client.ui.Image;
/**
* Abstract superclass for button based mock components.
*
* @author lizlooney@google.com (Liz Looney)
*/
abstract class MockButtonBase extends MockVisibleComponent {
// Property names
private static final String PROPERTY_NAME_IMAGE = "Image";
// GWT widget used to mock a Simple Button
private final Button buttonWidget;
private int[] preferredSizeOfButton;
private final Image image;
private String imagePropValue;
private boolean hasImage;
// We need to maintain these so we can show color and shape only when
// there is no image.
private String backgroundColor;
// Legal values for shape are defined in
// com.google.appinventor.components.runtime.Component.java.
private int shape;
/**
* Creates a new MockButtonBase component.
*
* @param editor editor of source file the component belongs to
*/
MockButtonBase(SimpleEditor editor, String type, ImageResource icon) {
super(editor, type, icon);
// Initialize mock button UI
buttonWidget = new Button();
buttonWidget.addStyleName("ode-SimpleMockButton");
image = new Image();
image.addErrorHandler(new ErrorHandler() {
@Override
public void onError(ErrorEvent event) {
if (imagePropValue != null && !imagePropValue.isEmpty()) {
OdeLog.elog("Error occurred while loading image " + imagePropValue);
}
refreshForm();
}
});
image.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
refreshForm();
}
});
DeckPanel deckPanel = new DeckPanel();
deckPanel.setStylePrimaryName("ode-SimpleMockComponent");
deckPanel.add(buttonWidget);
deckPanel.add(image);
deckPanel.showWidget(0);
initComponent(deckPanel);
}
/**
* Class that extends Button so we can use a protected constructor.
*
* <p/>The purpose of this class is to create a clone of the Button passed to
* the constructor. It will be used to determine the preferred size of the
* Button, without having the size constrained by its parent, since the
* cloned Button won't have a parent.
*/
static class ClonedButton extends Button {
ClonedButton(Button b) {
// Get the Element from the Button.
// Call DOM.clone to make a deep clone of that element.
// Pass that cloned element to the super constructor.
super(DOM.clone(b.getElement(), true)); // true for a deep clone
}
}
private Button createClonedButton() {
return new ClonedButton(buttonWidget);
}
@Override
public void onCreateFromPalette() {
// Change button caption to component name
changeProperty(PROPERTY_NAME_TEXT, MESSAGES.textPropertyValue(getName()));
}
/*
* Sets the button's TextAlignment property to a new value.
*/
private void setTextAlignmentProperty(String text) {
MockComponentsUtil.setWidgetTextAlign(buttonWidget, text);
}
/*
* Sets the button's Shape property to a new value.
*/
private void setShapeProperty(String text) {
shape = Integer.parseInt(text);
// Android Buttons with images take the shape of the image and do not
// use one of the defined Shapes.
if (hasImage) {
return;
}
switch(shape) {
case 0:
// Default Button
DOM.setStyleAttribute(buttonWidget.getElement(), "borderRadius", "0px");
break;
case 1:
// Rounded Button.
// The corners of the Button are rounded by 10 px.
// The value 10 px was chosen strictly for style.
// 10 px is the same as ROUNDED_CORNERS_RADIUS defined in
// com.google.appinventor.components.runtime.ButtonBase.
DOM.setStyleAttribute(buttonWidget.getElement(), "borderRadius", "10px");
break;
case 2:
// Rectangular Button
DOM.setStyleAttribute(buttonWidget.getElement(), "borderRadius", "0px");
break;
case 3:
// Oval Button
String height = DOM.getStyleAttribute(buttonWidget.getElement(), "height");
DOM.setStyleAttribute(buttonWidget.getElement(), "borderRadius", height);
break;
default:
// This should never happen
throw new IllegalArgumentException("shape:" + shape);
}
}
/*
* Sets the button's BackgroundColor property to a new value.
*/
private void setBackgroundColorProperty(String text) {
backgroundColor = text;
// Android Buttons do not show a background color if they have an image.
if (hasImage) {
return;
}
if (MockComponentsUtil.isDefaultColor(text)) {
// CSS background-color for ode-SimpleMockButton (copied from Ya.css)
text = "&HFFE8E8E8";
}
MockComponentsUtil.setWidgetBackgroundColor(buttonWidget, text);
}
/*
* Sets the button's Enabled property to a new value.
*/
private void setEnabledProperty(String text) {
MockComponentsUtil.setEnabled(this, text);
}
/*
* Sets the button's FontBold property to a new value.
*/
private void setFontBoldProperty(String text) {
MockComponentsUtil.setWidgetFontBold(buttonWidget, text);
updatePreferredSizeOfButton();
}
/*
* Sets the button's FontItalic property to a new value.
*/
private void setFontItalicProperty(String text) {
MockComponentsUtil.setWidgetFontItalic(buttonWidget, text);
updatePreferredSizeOfButton();
}
/*
* Sets the button's FontSize property to a new value.
*/
private void setFontSizeProperty(String text) {
MockComponentsUtil.setWidgetFontSize(buttonWidget, text);
updatePreferredSizeOfButton();
}
/*
* Sets the button's FontTypeface property to a new value.
*/
private void setFontTypefaceProperty(String text) {
MockComponentsUtil.setWidgetFontTypeface(buttonWidget, text);
updatePreferredSizeOfButton();
}
/*
* Sets the button's Image property to a new value.
*/
private void setImageProperty(String text) {
imagePropValue = text;
String url = convertImagePropertyValueToUrl(text);
if (url == null) {
hasImage = false;
url = "";
setBackgroundColorProperty(backgroundColor);
setShapeProperty(Integer.toString(shape));
} else {
hasImage = true;
// Android Buttons do not show a background color if they have an image.
// The container's background color shows through any transparent
// portions of the Image, an effect we can get in the browser by
// setting the widget's background color to COLOR_NONE.
MockComponentsUtil.setWidgetBackgroundColor(buttonWidget,
"&H" + COLOR_NONE);
DOM.setStyleAttribute(buttonWidget.getElement(), "borderRadius", "0px");
}
MockComponentsUtil.setWidgetBackgroundImage(buttonWidget, url);
image.setUrl(url);
}
/*
* Sets the button's Text property to a new value.
*/
private void setTextProperty(String text) {
buttonWidget.setText(text);
updatePreferredSizeOfButton();
}
/*
* Sets the button's TextColor property to a new value.
*/
private void setTextColorProperty(String text) {
if (MockComponentsUtil.isDefaultColor(text)) {
text = "&HFF000000"; // black
}
MockComponentsUtil.setWidgetTextColor(buttonWidget, text);
}
private final void updatePreferredSizeOfButton() {
preferredSizeOfButton = MockComponentsUtil.getPreferredSizeOfDetachedWidget(
createClonedButton());
}
@Override
public int getPreferredWidth() {
// The superclass uses getOffsetWidth of the DeckPanel, which won't work for us.
if (preferredSizeOfButton == null) {
updatePreferredSizeOfButton();
}
int width = preferredSizeOfButton[0];
if (hasImage) {
int imageWidth = image.getWidth();
if (imageWidth > width) {
width = imageWidth;
}
}
return width;
}
@Override
public int getPreferredHeight() {
// The superclass uses getOffsetHeight of the DeckPanel, which won't work for us.
if (preferredSizeOfButton == null) {
updatePreferredSizeOfButton();
}
int height = preferredSizeOfButton[1];
if (hasImage) {
int imageHeight = image.getHeight();
if (imageHeight > height) {
height = imageHeight;
}
}
return height;
}
// PropertyChangeListener implementation
@Override
public void onPropertyChange(String propertyName, String newValue) {
super.onPropertyChange(propertyName, newValue);
// Apply changed properties to the mock component
if (propertyName.equals(PROPERTY_NAME_TEXTALIGNMENT)) {
setTextAlignmentProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_BACKGROUNDCOLOR)) {
setBackgroundColorProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_ENABLED)) {
setEnabledProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_FONTBOLD)) {
setFontBoldProperty(newValue);
refreshForm();
} else if (propertyName.equals(PROPERTY_NAME_FONTITALIC)) {
setFontItalicProperty(newValue);
refreshForm();
} else if (propertyName.equals(PROPERTY_NAME_FONTSIZE)) {
setFontSizeProperty(newValue);
refreshForm();
} else if (propertyName.equals(PROPERTY_NAME_FONTTYPEFACE)) {
setFontTypefaceProperty(newValue);
refreshForm();
} else if (propertyName.equals(PROPERTY_NAME_IMAGE)) {
setImageProperty(newValue);
refreshForm();
} else if (propertyName.equals(PROPERTY_NAME_TEXT)) {
setTextProperty(newValue);
refreshForm();
} else if (propertyName.equals(PROPERTY_NAME_TEXTCOLOR)) {
setTextColorProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_BUTTONSHAPE)){
setShapeProperty(newValue);
}
}
}