com.servoy.j2db.server.headlessclient.dataui.WebImageBeanHolder.java Source code

Java tutorial

Introduction

Here is the source code for com.servoy.j2db.server.headlessclient.dataui.WebImageBeanHolder.java

Source

/*
 This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
    
 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU Affero General Public License as published by the Free
 Software Foundation; either version 3 of the License, or (at your option) any
 later version.
    
 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
    
 You should have received a copy of the GNU Affero General Public License along
 with this program; if not, see http://www.gnu.org/licenses or write to the Free
 Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 */
package com.servoy.j2db.server.headlessclient.dataui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Date;

import javax.swing.JComponent;
import javax.swing.JPanel;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.IResourceListener;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.model.AbstractReadOnlyModel;

import com.servoy.j2db.IApplication;
import com.servoy.j2db.persistence.IAnchorConstants;
import com.servoy.j2db.ui.IStylePropertyChanges;
import com.servoy.j2db.ui.scripting.RuntimeScriptButton;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.IDelegate;
import com.servoy.j2db.util.Utils;
import com.servoy.j2db.util.gui.SnapShot;
import com.servoy.j2db.util.gui.SnapShot.IMAGE_TYPE;

/**
 * Shows an image from a bean running in the server
 * @author jcompagner
 */
public class WebImageBeanHolder extends WebBaseButton implements IDelegate {
    private static final long serialVersionUID = 1L;

    private final JComponent bean;

    // Fields used for caching data sent to web client. The image of the bean is sent
    // only if there is any change since the previously sent image.
    private boolean lastChanged;
    private byte[] lastSnapshot;
    private Date lastIsChangedQuery;

    private final int anchoring;

    public WebImageBeanHolder(IApplication application, RuntimeScriptButton scriptable, String id, JComponent bean,
            int anchoring) {
        super(application, scriptable, id, ""); //$NON-NLS-1$
        ((ChangesRecorder) scriptable.getChangesRecorder()).setDefaultBorderAndPadding(null, null);
        this.bean = bean;
        this.anchoring = anchoring;
        if (bean != null)
            bean.addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    if (!WebImageBeanHolder.this.getSize().equals(WebImageBeanHolder.this.bean.getSize())) {
                        WebImageBeanHolder.this.getScriptObject().setSize(WebImageBeanHolder.this.bean.getWidth(),
                                WebImageBeanHolder.this.bean.getHeight());
                    }
                }
            });
        setMediaOption(8 + 1);

        add(new AttributeModifier("src", new AbstractReadOnlyModel<String>() //$NON-NLS-1$
        {
            private static final long serialVersionUID = 1L;

            @Override
            public String getObject() {
                return urlFor(IResourceListener.INTERFACE) + "&x=" + Math.random(); //$NON-NLS-1$
            }

        }));

        icon = new MediaResource();

        final boolean useAnchors = Utils.getAsBoolean(application.getRuntimeProperties().get("enableAnchors")); //$NON-NLS-1$
        if (useAnchors) {
            if ((anchoring & (IAnchorConstants.WEST | IAnchorConstants.EAST)) != 0
                    || (anchoring & (IAnchorConstants.NORTH | IAnchorConstants.SOUTH)) != 0) {
                add(new AbstractServoyDefaultAjaxBehavior() {
                    @Override
                    public void renderHead(IHeaderResponse response) {
                        super.renderHead(response);

                        String beanHolderId = WebImageBeanHolder.this.getMarkupId();

                        int width = getSize().width;
                        int height = getSize().height;

                        StringBuffer sb = new StringBuffer();
                        sb.append("if(typeof(beansPreferredSize) != \"undefined\")\n").append("{\n"); //$NON-NLS-1$ //$NON-NLS-2$
                        sb.append("beansPreferredSize['").append(beanHolderId).append("'] = new Array();\n"); //$NON-NLS-1$ //$NON-NLS-2$
                        sb.append("beansPreferredSize['").append(beanHolderId).append("']['height'] = ") //$NON-NLS-1$//$NON-NLS-2$
                                .append(height).append(";\n"); //$NON-NLS-1$
                        sb.append("beansPreferredSize['").append(beanHolderId).append("']['width'] = ") //$NON-NLS-1$//$NON-NLS-2$
                                .append(width).append(";\n"); //$NON-NLS-1$
                        sb.append("beansPreferredSize['").append(beanHolderId).append("']['callback'] = '") //$NON-NLS-1$//$NON-NLS-2$
                                .append(getCallbackUrl()).append("';\n"); //$NON-NLS-1$
                        sb.append("}\n"); //$NON-NLS-1$
                        response.renderOnLoadJavascript(sb.toString());
                    }

                    @Override
                    protected void respond(AjaxRequestTarget target) {
                        String sWidthHint = getComponent().getRequest().getParameter("width"); //$NON-NLS-1$ 
                        String sHeightHint = getComponent().getRequest().getParameter("height"); //$NON-NLS-1$ 
                        int widthHint = Integer.parseInt(sWidthHint);
                        int heightHint = Integer.parseInt(sHeightHint);

                        setSize(new Dimension(widthHint, heightHint));
                        WebEventExecutor.generateResponse(target, getComponent().getPage());
                    }
                });
            }
        }
    }

    public Object getDelegate() {
        return bean;
    }

    @Override
    public void onSubmit() {
        super.onSubmit();
        try {
            int x = Utils.getAsInteger(getRequest().getParameter(getInputName() + ".x")); //$NON-NLS-1$
            int y = Utils.getAsInteger(getRequest().getParameter(getInputName() + ".y")); //$NON-NLS-1$
            bean.dispatchEvent(
                    new MouseEvent(bean, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, x, y, 1, false));
        } catch (Exception e) {
            Debug.error(e);
        }
    }

    @Override
    public void onResourceRequested() {
        Dimension size = getSize();
        if (size != null) {
            createBeanIcon(size);
            super.onResourceRequested();
        }
    }

    private void createBeanIcon(Dimension size) {
        try {
            // Try to get a new icon, if there were any changes. If there
            // was no change, then null will be returned.
            byte[] mostRecentIcon = getIconIfChanged(size);
            if (mostRecentIcon != null) {
                setIcon(mostRecentIcon);
                if (icon != null) {
                    icon.setCacheable(false);
                }
            }
        } catch (Exception e) {
            Debug.log("Error rendering bean in web client: " + bean, e); //$NON-NLS-1$
        }
    }

    private byte[] getIconIfChanged(Dimension size) {
        // It seems that the web client generates invocations in pairs: first the "isChanged" method
        // is invoked, then the "onResourceRequested". The following situation may appear: when the 
        // "isChanged" method is called, it detects a change, but by the time we get to "onResourceRequested"
        // there is no new change. In this situation we have to remember the result from the "isChanged".
        // On the other hand, sometimes the "onResourceRequested" method can be invoked independently of 
        // "isChanged", in which case we don't have to rely on the results from previous calls to "isChanged".
        // So, as a compromise, we do the following: we rely on a previous call to "isChanged" only if it
        // was invoked in the recent past (last 2 seconds). Otherwise we assume any cached data may be
        // out dated and we check again for changed in "onResourceRequested".
        Date now = new Date();
        if ((lastIsChangedQuery == null) || (now.getTime() - lastIsChangedQuery.getTime() > 2000))
            createSnapshot(size);
        if (lastChanged)
            return lastSnapshot;
        else
            return null;
    }

    private void createSnapshot(Dimension size) {
        JPanel parent = new JPanel(new BorderLayout()) {
            private static final long serialVersionUID = 1L;

            @Override
            public Image createImage(int width, int height) {
                return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
            }
        };

        parent.setOpaque(false);
        parent.add(bean, BorderLayout.CENTER);
        parent.setSize(size);
        parent.doLayout();
        parent.addNotify();

        byte[] mostRecent = SnapShot.createImage(null, parent, size.width, size.height, IMAGE_TYPE.PNG);

        parent.remove(bean);

        if (mostRecent != null) {
            if (lastSnapshot != null) {
                if (mostRecent.length != lastSnapshot.length) {
                    lastChanged = true;
                } else {
                    lastChanged = false;
                    for (int i = 0; i < mostRecent.length; i++) {
                        if (mostRecent[i] != lastSnapshot[i]) {
                            lastChanged = true;
                            break;
                        }
                    }
                }
            } else {
                lastChanged = true;
            }
        } else {
            lastChanged = false;
        }

        if (lastChanged)
            lastSnapshot = mostRecent;
    }

    /**
     * @see com.servoy.j2db.server.headlessclient.dataui.WebBaseButton#getStylePropertyChanges()
     */
    @Override
    public IStylePropertyChanges getStylePropertyChanges() {
        IStylePropertyChanges changes = super.getStylePropertyChanges();
        if (!lastChanged) {
            testChanged();
            if (!changes.isChanged() && lastChanged) {
                changes.setChanged();
            }
        } else {
            changes.setChanged();
        }
        boolean useAnchors = Utils.getAsBoolean(application.getRuntimeProperties().get("enableAnchors")); //$NON-NLS-1$
        if (useAnchors) {
            if ((anchoring & (IAnchorConstants.EAST | IAnchorConstants.WEST)) != 0)
                changes.getChanges().remove("width"); //$NON-NLS-1$
            if ((anchoring & (IAnchorConstants.NORTH | IAnchorConstants.SOUTH)) != 0)
                changes.getChanges().remove("height"); //$NON-NLS-1$
        }
        return changes;
    }

    @Override
    public boolean isVisible() {
        return bean.isVisible();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.servoy.j2db.server.headlessclient.dataui.WebBaseButton#setComponentVisible(boolean)
     */
    @Override
    public void setComponentVisible(boolean visible) {
        bean.setVisible(visible);
    }

    @Override
    public void setSize(Dimension size) {
        createBeanIcon(size);
        super.setSize(size);
    }

    private boolean testChanged() {
        try {
            // Here we check for changes all the time. This is the purpose of this method, to query any
            // new changes, so we cannot cache results.
            Dimension size = getSize();
            if (size != null) {
                createSnapshot(getSize());
                lastIsChangedQuery = new Date(); // Remember the time of this query for changes.
                return lastChanged;
            } else {
                return false;
            }
        } catch (Exception e) {
            Debug.log("Error checking if bean in web client was changed: " + bean, e);
            return false;
        }
    }

    @Override
    protected void addEnabledStyleAttributeModifier() {
        // ignore
    }
}