/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.google.gdt.eclipse.designer.uibinder.model.widgets;
import com.google.gdt.eclipse.designer.model.widgets.IUIObjectInfo;
import com.google.gdt.eclipse.designer.model.widgets.IUIObjectSizeSupport;
import com.google.gdt.eclipse.designer.model.widgets.support.DOMUtils;
import com.google.gdt.eclipse.designer.model.widgets.support.GwtState;
import com.google.gdt.eclipse.designer.model.widgets.support.UIObjectUtils;
import com.google.gdt.eclipse.designer.uibinder.model.util.NameProperty;
import com.google.gdt.eclipse.designer.uibinder.parser.UiBinderContext;
import com.google.gdt.eclipse.designer.uibinder.parser.UiBinderRenderer;
import org.eclipse.wb.draw2d.geometry.Insets;
import org.eclipse.wb.draw2d.geometry.Rectangle;
import org.eclipse.wb.draw2d.geometry.Translatable;
import org.eclipse.wb.internal.core.model.property.Property;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.ui.UiUtils;
import org.eclipse.wb.internal.core.xml.model.AbstractComponentInfo;
import org.eclipse.wb.internal.core.xml.model.EditorContext;
import org.eclipse.wb.internal.core.xml.model.creation.CreationSupport;
import org.eclipse.wb.internal.core.xml.model.description.ComponentDescription;
import org.eclipse.wb.internal.core.xml.model.utils.XmlObjectUtils;
import org.eclipse.swt.graphics.Image;
import org.apache.commons.lang.StringUtils;
import java.util.List;
/**
* Model for <code>UiObject</code> in GWT UiBinder.
*
* @author scheglov_ke
* @coverage GWT.UiBinder.model
*/
public class UIObjectInfo extends AbstractComponentInfo implements IUIObjectInfo {
private final Property m_variableProperty = new NameProperty(this);
////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
////////////////////////////////////////////////////////////////////////////
public UIObjectInfo(EditorContext context,
ComponentDescription description,
CreationSupport creationSupport) throws Exception {
super(context, description, creationSupport);
}
////////////////////////////////////////////////////////////////////////////
//
// Access
//
////////////////////////////////////////////////////////////////////////////
@Override
public UiBinderContext getContext() {
return (UiBinderContext) super.getContext();
}
public final GwtState getState() {
return getContext().getState();
}
/**
* @return the {@link UIObjectUtils} to work with <code>UIObject</code>.
*/
public final UIObjectUtils getUIObjectUtils() {
return getState().getUIObjectUtils();
}
/**
* @return the {@link DOMUtils} to work with <code>Element</code>.
*/
public final DOMUtils getDOM() {
return getState().getDomUtils();
}
@Override
public Rectangle getAbsoluteBounds() {
return m_absoluteBounds;
}
/**
* @return the DOM margins.
*/
public final Insets getMargins() {
return m_margins;
}
/**
* @return the DOM borders.
*/
public final Insets getBorders() {
return m_borders;
}
/**
* @return the DOM paddings.
*/
public final Insets getPaddings() {
return m_paddings;
}
/**
* @return the underlying DOM <code>Element</code>.
*/
public Object getDOMElement() throws Exception {
return getState().getUIObjectUtils().getElement(getObject());
}
/**
* @return <code>true</code> if this {@link UIObjectInfo} has no visible border, so artificial
* border should be drawn.
*/
@Override
public boolean shouldDrawDotsBorder() {
return ExecutionUtils.runObjectIgnore(new RunnableObjectEx<Boolean>() {
public Boolean runObject() throws Exception {
String script = XmlObjectUtils.getParameter(UIObjectInfo.this, "shouldDrawBorder");
if (!StringUtils.isEmpty(script)) {
return (Boolean) XmlObjectUtils.executeScript(UIObjectInfo.this, script);
}
return false;
}
}, false);
}
////////////////////////////////////////////////////////////////////////////
//
// Size
//
////////////////////////////////////////////////////////////////////////////
private final UIObjectSizeSupport m_sizeSupport = new UIObjectSizeSupport(this);
public final IUIObjectSizeSupport getSizeSupport() {
return m_sizeSupport;
}
////////////////////////////////////////////////////////////////////////////
//
// Life cycle
//
////////////////////////////////////////////////////////////////////////////
@Override
protected final void saveEdit() throws Exception {
super.saveEdit();
getContext().saveFormEditor();
}
////////////////////////////////////////////////////////////////////////////
//
// Properties
//
////////////////////////////////////////////////////////////////////////////
@Override
protected List<Property> getPropertyList() throws Exception {
List<Property> properties = super.getPropertyList();
properties.add(m_sizeSupport.getSizeProperty());
properties.add(m_variableProperty);
return properties;
}
////////////////////////////////////////////////////////////////////////////
//
// Refresh
//
////////////////////////////////////////////////////////////////////////////
private List<Object> m_liveProcessHiddenWidgets;
@Override
public void refresh_dispose() throws Exception {
// GWT does not allow any operation after disposing GWTState
if (getState().isDisposed()) {
return;
}
// clear RootPanel
if (isRoot()) {
getUIObjectUtils().clearRootPanel();
}
// dispose children
super.refresh_dispose();
}
@Override
public void refresh_beforeCreate() throws Exception {
call_Impl_enter();
super.refresh_beforeCreate();
}
@Override
protected void refresh_create() throws Exception {
UiBinderRenderer renderer = new UiBinderRenderer(this);
renderer.render();
}
@Override
protected void refresh_afterCreate() throws Exception {
super.refresh_afterCreate();
if (isRoot()) {
UIObjectUtils utils = getUIObjectUtils();
Object liveWidget = utils.getLiveWidget();
if (liveWidget != null) {
m_liveProcessHiddenWidgets = utils.hideRootPanelWidgets(liveWidget);
}
}
}
@Override
protected void refresh_afterCreate2() throws Exception {
super.refresh_afterCreate2();
call_Impl_exit();
}
@Override
protected void refresh_finish() throws Exception {
super.refresh_finish();
if (isRoot()) {
if (m_liveProcessHiddenWidgets != null) {
UIObjectUtils.showWidgets(m_liveProcessHiddenWidgets);
m_liveProcessHiddenWidgets = null;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// Impl life cycle
//
////////////////////////////////////////////////////////////////////////////
/**
* Call enter() as if this UI was create as result of call from JavaScript.
*/
private void call_Impl_enter() {
if (isRoot()) {
ExecutionUtils.runIgnore(new RunnableEx() {
public void run() throws Exception {
ClassLoader classLoader = getContext().getClassLoader();
Class<?> class_Impl = classLoader.loadClass("com.google.gwt.core.client.impl.Impl");
ReflectionUtils.invokeMethod(class_Impl, "enter()");
}
});
}
}
/**
* Call exit() as if this UI was create as result of call from JavaScript.
*/
private void call_Impl_exit() {
if (isRoot()) {
ExecutionUtils.runIgnore(new RunnableEx() {
public void run() throws Exception {
ClassLoader classLoader = getContext().getClassLoader();
Class<?> class_Impl = classLoader.loadClass("com.google.gwt.core.client.impl.Impl");
ReflectionUtils.invokeMethod(class_Impl, "exit(boolean)", true);
}
});
}
}
////////////////////////////////////////////////////////////////////////////
//
// Refresh: fetch
//
////////////////////////////////////////////////////////////////////////////
private Rectangle m_absoluteBounds;
protected Insets m_margins;
protected Insets m_borders;
protected Insets m_paddings;
@Override
protected void refresh_fetch() throws Exception {
Object element = getDOMElement();
// check for messages loop
if (isRoot()) {
if (requiresMessagesLoop()) {
for (int i = 0; i < 5; i++) {
getState().runMessagesLoop();
Thread.sleep(1);
}
}
}
// prepare bounds
fetchBounds(element);
// prepare image
if (isRoot()) {
fetchImage(element);
}
// prepare parent-relative bounds
{
Rectangle parentBounds = new Rectangle(m_absoluteBounds);
if (getParent() instanceof AbstractComponentInfo) {
AbstractComponentInfo parent = (AbstractComponentInfo) getParent();
Rectangle parentAbsoluteBounds = parent.getAbsoluteBounds();
parentBounds.x -= parentAbsoluteBounds.x;
parentBounds.y -= parentAbsoluteBounds.y;
} else {
parentBounds.x = parentBounds.y = 0;
}
setBounds(parentBounds);
}
// borders/margins/paddings
fetchSpacing();
// continue for children
super.refresh_fetch();
}
protected void fetchBounds(Object element) throws Exception {
m_absoluteBounds = fetchAbsoluteBounds(element);
{
Rectangle bounds = m_absoluteBounds.getCopy();
if (getParent() instanceof AbstractComponentInfo) {
AbstractComponentInfo parent = (AbstractComponentInfo) getParent();
Rectangle parentAbsoluteBounds = parent.getAbsoluteBounds();
bounds.x -= parentAbsoluteBounds.x;
bounds.y -= parentAbsoluteBounds.y;
bounds.translate(parent.getClientAreaInsets().getNegated());
}
setModelBounds(bounds);
}
}
protected Rectangle fetchModelBounds(Object element) {
return getState().getModelBounds(element);
}
protected Rectangle fetchAbsoluteBounds(Object element) {
Rectangle absoluteBounds = getState().getAbsoluteBounds(element);
// Case 43858: https://fogbugz.instantiations.com/default.php?43858
if (isRoot()) {
if (absoluteBounds.x < 0) {
absoluteBounds.x = 0;
}
if (absoluteBounds.y < 0) {
absoluteBounds.y = 0;
}
}
return absoluteBounds;
}
protected void fetchImage(Object element) throws Exception {
GwtState state = getState();
Image browserScreenshot = state.createBrowserScreenshot();
// prepare bounds of image
Rectangle imageBounds = m_absoluteBounds;
if (state.isStrictMode() && state.isBrowserExplorer()) {
imageBounds = imageBounds.getTranslated(2, 2);
}
// set image
Image objectImage = UiUtils.getCroppedImage(browserScreenshot, imageBounds.getSwtRectangle());
setImage(objectImage);
}
/**
* @return <code>true</code> if this {@link UIObjectInfo} or one of its children require running
* messages loop before {@link #refresh_fetch()}, to finalize rendering.
*/
private boolean requiresMessagesLoop() {
for (UIObjectInfo child : getChildren(UIObjectInfo.class)) {
if (child.requiresMessagesLoop()) {
return true;
}
}
{
String script = XmlObjectUtils.getParameter(this, "GWT.requiresMessagesLoop");
return getUIObjectUtils().evaluateScriptBoolean(script);
}
}
/**
* Fetch spacing of UI Object.
*/
protected void fetchSpacing() throws Exception {
GwtState state = getState();
Object element = getDOMElement();
m_margins = state.getMargins(element);
m_borders = state.getBorders(element);
m_paddings = state.getPaddings(element);
// prepare "clientAreaInsets"
if (!getState().isBody(element)) {
fetchClientAreaInsets();
}
}
/**
* Fetches "client area insets". Usually "margins + borders", but complex components may require
* special handling.
*/
protected void fetchClientAreaInsets() throws Exception {
Insets insets = new Insets();
insets.add(m_margins);
insets.add(m_borders);
setClientAreaInsets(insets);
}
/**
* Transforms given absolute {@link Translatable} into this {@link UIObjectInfo} relative.
*/
public final void absoluteToRelative(Translatable translatable) {
translatable.translate(-m_absoluteBounds.x, -m_absoluteBounds.y);
}
/**
* Transforms given absolute {@link Translatable} into this {@link UIObjectInfo} model.
*/
public final void absoluteToModel(Translatable translatable) {
absoluteToRelative(translatable);
{
Insets clientAreaInsets = getClientAreaInsets();
translatable.translate(-clientAreaInsets.left, -clientAreaInsets.top);
}
}
}