All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.v7.client.ui.VUpload Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */

package com.vaadin.v7.client.ui;

import java.util.logging.Logger;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.FormElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.ui.VButton;
import com.vaadin.v7.client.ui.upload.UploadConnector;
import com.vaadin.v7.client.ui.upload.UploadIFrameOnloadStrategy;
import com.vaadin.v7.shared.ui.upload.UploadServerRpc;

/**
 *
 * Note, we are not using GWT FormPanel as we want to listen submitcomplete
 * events even though the upload component is already detached.
 *
 */
public class VUpload extends SimplePanel {

    private final class MyFileUpload extends FileUpload {
        @Override
        public void onBrowserEvent(Event event) {
            super.onBrowserEvent(event);
            if (event.getTypeInt() == Event.ONCHANGE) {
                if (immediate && fu.getFilename() != null
                        && !"".equals(fu.getFilename())) {
                    submit();
                }
            } else if (BrowserInfo.get().isIE()
                    && event.getTypeInt() == Event.ONFOCUS) {
                // IE and user has clicked on hidden textarea part of upload
                // field. Manually open file selector, other browsers do it by
                // default.
                fireNativeClick(fu.getElement());
                // also remove focus to enable hack if user presses cancel
                // button
                fireNativeBlur(fu.getElement());
            }
        }
    }

    public static final String CLASSNAME = "v-upload";

    /**
     * FileUpload component that opens native OS dialog to select file.
     * 

* For internal use only. May be removed or replaced in the future. */ public FileUpload fu = new MyFileUpload(); Panel panel = new FlowPanel(); UploadIFrameOnloadStrategy onloadstrategy = GWT .create(UploadIFrameOnloadStrategy.class); /** For internal use only. May be removed or replaced in the future. */ public ApplicationConnection client; /** For internal use only. May be removed or replaced in the future. */ public String paintableId; /** * Button that initiates uploading. *

* For internal use only. May be removed or replaced in the future. */ public final VButton submitButton; /** * When expecting big files, programmer may initiate some UI changes when * uploading the file starts. Bit after submitting file we'll visit the * server to check possible changes. *

* For internal use only. May be removed or replaced in the future. */ public Timer t; /** * some browsers tries to send form twice if submit is called in button * click handler, some don't submit at all without it, so we need to track * if form is already being submitted */ private boolean submitted = false; private boolean enabled = true; private boolean immediate; private Hidden maxfilesize = new Hidden(); /** For internal use only. May be removed or replaced in the future. */ public FormElement element; private com.google.gwt.dom.client.Element synthesizedFrame; /** For internal use only. May be removed or replaced in the future. */ public int nextUploadId; public VUpload() { super(com.google.gwt.dom.client.Document.get().createFormElement()); element = getElement().cast(); setEncoding(getElement(), FormPanel.ENCODING_MULTIPART); element.setMethod(FormPanel.METHOD_POST); setWidget(panel); panel.add(maxfilesize); panel.add(fu); submitButton = new VButton(); submitButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (immediate) { // fire click on upload (e.g. focused button and hit space) fireNativeClick(fu.getElement()); } else { submit(); } } }); panel.add(submitButton); setStyleName(CLASSNAME); } private static native void setEncoding(Element form, String encoding) /*-{ form.enctype = encoding; // IE8 not supported in Vaadin 8 }-*/; /** For internal use only. May be removed or replaced in the future. */ public void setImmediate(boolean booleanAttribute) { if (immediate != booleanAttribute) { immediate = booleanAttribute; if (immediate) { fu.sinkEvents(Event.ONCHANGE); fu.sinkEvents(Event.ONFOCUS); } } setStyleName(getElement(), CLASSNAME + "-immediate", immediate); } private static native void fireNativeClick(Element element) /*-{ element.click(); }-*/; private static native void fireNativeBlur(Element element) /*-{ element.blur(); }-*/; /** For internal use only. May be removed or replaced in the future. */ public void disableUpload() { setEnabledForSubmitButton(false); if (!submitted) { // Cannot disable the fileupload while submitting or the file won't // be submitted at all fu.getElement().setPropertyBoolean("disabled", true); } enabled = false; } /** For internal use only. May be removed or replaced in the future. */ public void enableUpload() { setEnabledForSubmitButton(true); fu.getElement().setPropertyBoolean("disabled", false); enabled = true; if (submitted) { /* * An old request is still in progress (most likely cancelled), * ditching that target frame to make it possible to send a new * file. A new target frame is created later." */ cleanTargetFrame(); submitted = false; } } private void setEnabledForSubmitButton(boolean enabled) { submitButton.setEnabled(enabled); submitButton.setStyleName(StyleConstants.DISABLED, !enabled); } /** * Re-creates file input field and populates panel. This is needed as we * want to clear existing values from our current file input field. */ private void rebuildPanel() { panel.remove(submitButton); panel.remove(fu); fu = new MyFileUpload(); fu.setName(paintableId + "_file"); fu.getElement().setPropertyBoolean("disabled", !enabled); panel.add(fu); panel.add(submitButton); if (immediate) { fu.sinkEvents(Event.ONCHANGE); } } /** * Called by JSNI (hooked via {@link #onloadstrategy}) */ private void onSubmitComplete() { /* Needs to be run dereferred to avoid various browser issues. */ Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { if (submitted) { if (client != null) { if (t != null) { t.cancel(); } getLogger().info("VUpload:Submit complete"); if (isAttached()) { // no need to call poll() if component is already // detached #8728 ((UploadConnector) ConnectorMap.get(client).getConnector(VUpload.this)) .getRpcProxy(UploadServerRpc.class).poll(); } } rebuildPanel(); submitted = false; enableUpload(); if (!isAttached()) { /* * Upload is complete when upload is already abandoned. */ cleanTargetFrame(); } } } }); } ScheduledCommand startUploadCmd = new ScheduledCommand() { @Override public void execute() { element.submit(); submitted = true; disableUpload(); /* * Visit server a moment after upload has started to see possible * changes from UploadStarted event. Will be cleared on complete. * * Must get the id here as the upload can finish before the timer * expires and in that case nextUploadId has been updated and is * wrong. */ final int thisUploadId = nextUploadId; t = new Timer() { @Override public void run() { // Only visit the server if the upload has not already // finished if (thisUploadId == nextUploadId) { getLogger().info( "Visiting server to see if upload started event changed UI."); client.updateVariable(paintableId, "pollForStart", thisUploadId, true); } } }; t.schedule(800); } }; /** For internal use only. May be removed or replaced in the future. */ public void submit() { if (submitted || !enabled) { getLogger().info("Submit cancelled (disabled or already submitted)"); return; } if (fu.getFilename().isEmpty()) { getLogger().info("Submitting empty selection (no file)"); } // flush possibly pending variable changes, so they will be handled // before upload client.sendPendingVariableChanges(); // This is done as deferred because sendPendingVariableChanges is also // deferred and we want to start the upload only after the changes have // been sent to the server Scheduler.get().scheduleDeferred(startUploadCmd); } /** For internal use only. May be removed or replaced in the future. */ public void disableTitle(boolean disable) { if (disable) { // Disable title attribute for upload element. if (BrowserInfo.get().isChrome()) { // In Chrome title has to be set to " " to make it invisible fu.setTitle(" "); } else if (BrowserInfo.get().isFirefox()) { // In FF title has to be set to empty string to make it // invisible // Method setTitle removes title attribute when it's an empty // string, so setAttribute() should be used here fu.getElement().setAttribute("title", ""); } // For other browsers absent title doesn't show default tooltip for // input element } else { fu.setTitle(null); } } @Override protected void onAttach() { super.onAttach(); if (client != null) { ensureTargetFrame(); } } /** For internal use only. May be removed or replaced in the future. */ public void ensureTargetFrame() { if (synthesizedFrame == null) { // Attach a hidden IFrame to the form. This is the target iframe to // which the form will be submitted. We have to create the iframe // using innerHTML, because setting an iframe's 'name' property // dynamically doesn't work on most browsers. DivElement dummy = Document.get().createDivElement(); dummy.setInnerHTML("