Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * 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.haulmont.cuba.web.widgets.client.jqueryfileupload; import com.google.gwt.aria.client.Id; import com.google.gwt.aria.client.RelevantValue; import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.event.dom.client.*; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.FlowPanel; import com.vaadin.client.BrowserInfo; import com.vaadin.client.ComputedStyle; import com.vaadin.client.Focusable; import com.vaadin.client.WidgetUtil; import com.vaadin.client.ui.*; import com.vaadin.client.ui.aria.AriaHelper; import static com.vaadin.client.WidgetUtil.isFocusedElementEditable; public class CubaFileUploadProgressWindow extends VOverlay implements KeyDownHandler, KeyUpHandler, Focusable { public static final String CLASSNAME = "v-window"; private static final String MODAL_WINDOW_OPEN_CLASSNAME = "v-modal-window-open"; public static final int Z_INDEX = 15000; protected Element contents; protected Element header; protected Element footer; protected Element resizeBox; protected SimpleFocusablePanel contentPanel; protected boolean dragging; protected int startX; protected int startY; protected int origX; protected int origY; protected boolean resizing; protected int origW; protected int origH; protected Element closeBox; protected boolean vaadinModality = false; protected boolean resizable = true; protected boolean draggable = false; protected Element modalityCurtain; protected Element draggingCurtain; protected Element resizingCurtain; protected Element headerText; protected boolean closable = true; protected Element topTabStop; protected Element bottomTabStop; protected Event.NativePreviewHandler topEventBlocker; protected Event.NativePreviewHandler bottomEventBlocker; protected HandlerRegistration topBlockerRegistration; protected HandlerRegistration bottomBlockerRegistration; // Prevents leaving the window with the Tab key when true protected boolean doTabStop; protected Element wrapper; protected boolean visibilityChangesDisabled; protected CloseListener closeListener; protected VLabel currentFileLabel; protected VButton cancelButton; protected VProgressBar progressBar; public CubaFileUploadProgressWindow() { super(false, true); // no autohide, modal // Different style of shadow for windows Roles.getDialogRole().set(getElement()); Roles.getDialogRole().setAriaRelevantProperty(getElement(), RelevantValue.ADDITIONS); constructDOM(); } @Override protected void onAttach() { super.onAttach(); /* * When this window gets reattached, set the tabstop to the previous * state. */ setTabStopEnabled(doTabStop); // Fix for #14413. Any pseudo elements inside these elements are not // visible on initial render unless we shake the DOM. if (BrowserInfo.get().isIE8()) { closeBox.getStyle().setDisplay(Style.Display.NONE); Scheduler.get().scheduleFinally(new Command() { @Override public void execute() { closeBox.getStyle().clearDisplay(); } }); } } @Override protected void onDetach() { super.onDetach(); removeTabBlockHandlers(); } private void addTabBlockHandlers() { if (topBlockerRegistration == null) { topBlockerRegistration = Event.addNativePreviewHandler(topEventBlocker); bottomBlockerRegistration = Event.addNativePreviewHandler(bottomEventBlocker); } } private void removeTabBlockHandlers() { if (topBlockerRegistration != null) { topBlockerRegistration.removeHandler(); topBlockerRegistration = null; bottomBlockerRegistration.removeHandler(); bottomBlockerRegistration = null; } } @Override protected void setZIndex(int zIndex) { super.setZIndex(zIndex); if (vaadinModality) { getModalityCurtain().getStyle().setZIndex(zIndex); } } protected Element getModalityCurtain() { if (modalityCurtain == null) { modalityCurtain = DOM.createDiv(); modalityCurtain.setClassName(CLASSNAME + "-modalitycurtain"); } return modalityCurtain; } protected void constructDOM() { setStyleName(CLASSNAME); topTabStop = DOM.createDiv(); topTabStop.setTabIndex(0); header = DOM.createDiv(); header.setClassName(CLASSNAME + "-outerheader"); headerText = DOM.createDiv(); headerText.setClassName(CLASSNAME + "-header"); contents = DOM.createDiv(); contents.setClassName(CLASSNAME + "-contents"); footer = DOM.createDiv(); footer.setClassName(CLASSNAME + "-footer"); resizeBox = DOM.createDiv(); resizeBox.setClassName(CLASSNAME + "-resizebox"); closeBox = DOM.createDiv(); closeBox.setClassName(CLASSNAME + "-closebox"); closeBox.setTabIndex(0); DOM.appendChild(footer, resizeBox); bottomTabStop = DOM.createDiv(); bottomTabStop.setTabIndex(0); wrapper = DOM.createDiv(); wrapper.setClassName(CLASSNAME + "-wrap"); DOM.appendChild(wrapper, topTabStop); DOM.appendChild(wrapper, header); DOM.appendChild(header, closeBox); DOM.appendChild(header, headerText); DOM.appendChild(wrapper, contents); DOM.appendChild(wrapper, footer); DOM.appendChild(wrapper, bottomTabStop); DOM.appendChild(super.getContainerElement(), wrapper); sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS | Event.ONCLICK | Event.ONLOSECAPTURE); currentFileLabel = new VLabel(); currentFileLabel.addStyleName("upload-file-label"); currentFileLabel.setWidth("100%"); progressBar = new VProgressBar(); progressBar.addStyleName("upload-progressbar"); progressBar.setIndeterminate(false); progressBar.setState(0); progressBar.setWidth("100%"); cancelButton = new VButton(); cancelButton.addStyleName("upload-cancel-button"); cancelButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { closeWindow(); } }); contentPanel = new SimpleFocusablePanel(); contentPanel.setStyleName("content-pane"); contentPanel.addKeyDownHandler(this); contentPanel.addKeyUpHandler(this); setWidget(contentPanel); final FlowPanel verticalPanel = new FlowPanel(); verticalPanel.setStyleName("vertical-panel"); verticalPanel.addStyleName("v-widget"); verticalPanel.addStyleName("v-has-width"); verticalPanel.addStyleName("v-has-height"); verticalPanel.add(currentFileLabel); verticalPanel.add(progressBar); verticalPanel.add(cancelButton); contentPanel.setWidget(verticalPanel); // do some calculations for window layout Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { Style contentStyle = contents.getStyle(); ComputedStyle headerCs = new ComputedStyle(header); String headerHeight = headerCs.getProperty("height"); contentStyle.setProperty("paddingTop", headerHeight); contentStyle.setProperty("marginTop", "-" + headerHeight); ComputedStyle footerCs = new ComputedStyle(footer); String footerHeight = footerCs.getProperty("height"); contentStyle.setProperty("paddingBottom", footerHeight); contentStyle.setProperty("marginBottom", "-" + footerHeight); } }); // Make the closebox accessible for assistive devices Roles.getButtonRole().set(closeBox); Roles.getButtonRole().setAriaLabelProperty(closeBox, "close button"); // Provide the title to assistive devices AriaHelper.ensureHasId(headerText); Roles.getDialogRole().setAriaLabelledbyProperty(getElement(), Id.of(headerText)); // Handlers to Prevent tab to leave the window // and backspace to cause browser navigation topEventBlocker = new Event.NativePreviewHandler() { @Override public void onPreviewNativeEvent(Event.NativePreviewEvent event) { NativeEvent nativeEvent = event.getNativeEvent(); if (nativeEvent.getEventTarget().cast() == topTabStop && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB && nativeEvent.getShiftKey()) { nativeEvent.preventDefault(); } if (nativeEvent.getEventTarget().cast() == topTabStop && nativeEvent.getKeyCode() == KeyCodes.KEY_BACKSPACE) { nativeEvent.preventDefault(); } } }; bottomEventBlocker = new Event.NativePreviewHandler() { @Override public void onPreviewNativeEvent(Event.NativePreviewEvent event) { NativeEvent nativeEvent = event.getNativeEvent(); if (nativeEvent.getEventTarget().cast() == bottomTabStop && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB && !nativeEvent.getShiftKey()) { nativeEvent.preventDefault(); } if (nativeEvent.getEventTarget().cast() == bottomTabStop && nativeEvent.getKeyCode() == KeyCodes.KEY_BACKSPACE) { nativeEvent.preventDefault(); } } }; } @Override public void setVisible(boolean visible) { /* * Visibility with VWindow works differently than with other Paintables * in Vaadin. Invisible VWindows are not attached to DOM at all. Flag is * used to avoid visibility call from * ApplicationConnection.updateComponent(); */ if (!visibilityChangesDisabled) { super.setVisible(visible); } if (visible && BrowserInfo.get().requiresPositionAbsoluteOverflowAutoFix()) { /* * Shake up the DOM a bit to make the window shed unnecessary * scrollbars and resize correctly afterwards. The version fixing * ticket #11994 which was changing the size to 110% was replaced * with this due to ticket #12943 */ WidgetUtil.runWebkitOverflowAutoFix(contents.getFirstChildElement()); } } public void setDraggable(boolean draggable) { if (this.draggable == draggable) { return; } this.draggable = draggable; setCursorProperties(); } private void setCursorProperties() { if (!draggable) { header.getStyle().setProperty("cursor", "default"); footer.getStyle().setProperty("cursor", "default"); } else { header.getStyle().setProperty("cursor", ""); footer.getStyle().setProperty("cursor", ""); } } /** * Sets the closable state of the window. Additionally hides/shows the close * button according to the new state. * * @param closable true if the window can be closed by the user */ public void setClosable(boolean closable) { if (this.closable == closable) { return; } this.closable = closable; if (closable) { closeBox.setClassName(CLASSNAME + "-closebox"); } else { closeBox.setClassName(CLASSNAME + "-closebox " + CLASSNAME + "-closebox-disabled"); } } /** * Returns the closable state of the sub window. If the sub window is * closable a decoration (typically an X) is shown to the user. By clicking * on the X the user can close the window. * * @return true if the sub window is closable */ protected boolean isClosable() { return closable; } public void setCancelButtonCaption(String cancelButtonCaption) { cancelButton.setText(cancelButtonCaption); } public void setCurrentFileName(String currentFileName) { currentFileLabel.setText(currentFileName); } public String getCurrentFileName() { return currentFileLabel.getText(); } public void setProgress(float state) { progressBar.setState(state); } @Override public void show() { if (vaadinModality) { showModalityCurtain(); } super.show(); } @Override public void hide() { /* * If the window has a RichTextArea and the RTA is focused at the time * of hiding in IE8 only the window will have some problems returning * the focus to the correct place. Curiously the focus will be returned * correctly if clicking on the "close" button in the window header but * closing the window from a button for example in the window will fail. * Symptom described in #10776 * * The problematic part is that for the focus to be returned correctly * an input element needs to be focused in the root panel. Focusing some * other element apparently won't work. */ if (BrowserInfo.get().isIE8()) { fixIE8FocusCaptureIssue(); } if (vaadinModality) { hideModalityCurtain(); } super.hide(); } private void fixIE8FocusCaptureIssue() { Element e = DOM.createInputText(); Style elemStyle = e.getStyle(); elemStyle.setPosition(Style.Position.ABSOLUTE); elemStyle.setTop(-10, Style.Unit.PX); elemStyle.setWidth(0, Style.Unit.PX); elemStyle.setHeight(0, Style.Unit.PX); contentPanel.getElement().appendChild(e); e.focus(); contentPanel.getElement().removeChild(e); } public void setVaadinModality(boolean modality) { vaadinModality = modality; if (vaadinModality) { if (isAttached()) { showModalityCurtain(); } addTabBlockHandlers(); } else { if (modalityCurtain != null) { if (isAttached()) { hideModalityCurtain(); } modalityCurtain = null; } if (!doTabStop) { removeTabBlockHandlers(); } } } private void showModalityCurtain() { getModalityCurtain().getStyle().setZIndex(Z_INDEX); if (isShowing()) { getOverlayContainer().insertBefore(getModalityCurtain(), getElement()); } else { getOverlayContainer().appendChild(getModalityCurtain()); } Document.get().getBody().addClassName(MODAL_WINDOW_OPEN_CLASSNAME); } private void hideModalityCurtain() { Document.get().getBody().removeClassName(MODAL_WINDOW_OPEN_CLASSNAME); modalityCurtain.removeFromParent(); if (BrowserInfo.get().isIE()) { // IE leaks memory in certain cases unless we release the reference // (#9197) modalityCurtain = null; } } /* * Shows an empty div on top of all other content; used when moving, so that * iframes (etc) do not steal event. */ private void showDraggingCurtain() { getElement().getParentElement().insertBefore(getDraggingCurtain(), getElement()); } private void hideDraggingCurtain() { if (draggingCurtain != null) { draggingCurtain.removeFromParent(); } } /* * Shows an empty div on top of all other content; used when resizing, so * that iframes (etc) do not steal event. */ private void showResizingCurtain() { getElement().getParentElement().insertBefore(getResizingCurtain(), getElement()); } private void hideResizingCurtain() { if (resizingCurtain != null) { resizingCurtain.removeFromParent(); } } private Element getDraggingCurtain() { if (draggingCurtain == null) { draggingCurtain = createCurtain(); draggingCurtain.setClassName(CLASSNAME + "-draggingCurtain"); } return draggingCurtain; } private Element getResizingCurtain() { if (resizingCurtain == null) { resizingCurtain = createCurtain(); resizingCurtain.setClassName(CLASSNAME + "-resizingCurtain"); } return resizingCurtain; } private Element createCurtain() { Element curtain = DOM.createDiv(); curtain.getStyle().setPosition(Style.Position.ABSOLUTE); curtain.getStyle().setTop(0, Style.Unit.PX); curtain.getStyle().setLeft(0, Style.Unit.PX); curtain.getStyle().setWidth(100, Style.Unit.PCT); curtain.getStyle().setHeight(100, Style.Unit.PCT); curtain.getStyle().setZIndex(VOverlay.Z_INDEX); return curtain; } /** INTERNAL. May be removed or replaced in the future. */ public void setResizable(boolean resizability) { resizable = resizability; if (resizability) { footer.setClassName(CLASSNAME + "-footer"); resizeBox.setClassName(CLASSNAME + "-resizebox"); } else { footer.setClassName(CLASSNAME + "-footer " + CLASSNAME + "-footer-noresize"); resizeBox.setClassName(CLASSNAME + "-resizebox " + CLASSNAME + "-resizebox-disabled"); } } @Override public void setPopupPosition(int left, int top) { if (top < 0) { // ensure window is not moved out of browser window from top of the // screen top = 0; } super.setPopupPosition(left, top); } public void setCaption(String c) { setCaption(c, false); } public void setCaption(String c, boolean asHtml) { String html; if (asHtml) { html = c == null ? "" : c; } else { html = WidgetUtil.escapeHTML(c); } headerText.setInnerHTML(html); } @Override protected com.google.gwt.user.client.Element getContainerElement() { // in GWT 1.5 this method is used in PopupPanel constructor if (contents == null) { return super.getContainerElement(); } return DOM.asOld(contents); } private Event headerDragPending; @Override public void onBrowserEvent(final Event event) { boolean bubble = true; final int type = event.getTypeInt(); final Element target = DOM.eventGetTarget(event); if (resizing || resizeBox == target) { onResizeEvent(event); bubble = false; } else if (isClosable() && target == closeBox) { if (type == Event.ONCLICK) { closeWindow(); } bubble = false; } else if (header.isOrHasChild(target) && !dragging) { // dblclick handled in connector if (type != Event.ONDBLCLICK && draggable) { if (type == Event.ONMOUSEDOWN) { /* * Prevents accidental selection of window caption or * content. (#12726) */ event.preventDefault(); headerDragPending = event; } else if (type == Event.ONMOUSEMOVE && headerDragPending != null) { // ie won't work unless this is set here dragging = true; onDragEvent(headerDragPending); onDragEvent(event); headerDragPending = null; } else { headerDragPending = null; } bubble = false; } } else if (dragging || !contents.isOrHasChild(target)) { onDragEvent(event); bubble = false; } /* * If clicking on other than the content, move focus to the window. * After that this windows e.g. gets all keyboard shortcuts. */ if (type == Event.ONMOUSEDOWN && !contentPanel.getElement().isOrHasChild(target) && target != closeBox) { contentPanel.focus(); } if (!bubble) { event.stopPropagation(); } else { // Super.onBrowserEvent takes care of Handlers added by the // ClickEventHandler super.onBrowserEvent(event); } } protected void closeWindow() { hide(); if (closeListener != null) { closeListener.onClose(); } } private void onResizeEvent(Event event) { if (resizable && WidgetUtil.isTouchEventOrLeftMouseButton(event)) { switch (event.getTypeInt()) { case Event.ONMOUSEDOWN: case Event.ONTOUCHSTART: showResizingCurtain(); if (BrowserInfo.get().isIE()) { resizeBox.getStyle().setVisibility(Style.Visibility.HIDDEN); } resizing = true; startX = WidgetUtil.getTouchOrMouseClientX(event); startY = WidgetUtil.getTouchOrMouseClientY(event); origW = getElement().getOffsetWidth(); origH = getElement().getOffsetHeight(); DOM.setCapture(getElement()); event.preventDefault(); break; case Event.ONMOUSEUP: case Event.ONTOUCHEND: setSize(event); case Event.ONTOUCHCANCEL: DOM.releaseCapture(getElement()); case Event.ONLOSECAPTURE: hideResizingCurtain(); if (BrowserInfo.get().isIE()) { resizeBox.getStyle().clearVisibility(); } resizing = false; break; case Event.ONMOUSEMOVE: case Event.ONTOUCHMOVE: if (resizing) { setSize(event); event.preventDefault(); } break; default: event.preventDefault(); break; } } } private boolean cursorInsideBrowserContentArea(Event event) { if (event.getClientX() < 0 || event.getClientY() < 0) { // Outside to the left or above return false; } if (event.getClientX() > Window.getClientWidth() || event.getClientY() > Window.getClientHeight()) { // Outside to the right or below return false; } return true; } private void setSize(Event event) { if (!cursorInsideBrowserContentArea(event)) { // Only drag while cursor is inside the browser client area return; } int w = WidgetUtil.getTouchOrMouseClientX(event) - startX + origW; int h = WidgetUtil.getTouchOrMouseClientY(event) - startY + origH; w = Math.max(w, getMinWidth()); h = Math.max(h, getMinHeight()); setWidth(w + "px"); setHeight(h + "px"); } private int getMinHeight() { return getPixelValue(getElement().getStyle().getProperty("minHeight")); } private int getMinWidth() { return getPixelValue(getElement().getStyle().getProperty("minWidth")); } private static int getPixelValue(String size) { if (size == null || !size.endsWith("px")) { return -1; } else { return Integer.parseInt(size.substring(0, size.length() - 2)); } } @Override public void setWidth(String width) { // Override PopupPanel which sets the width to the contents getElement().getStyle().setProperty("width", width); // Update v-has-width in case undefined window is resized setStyleName("v-has-width", width != null && width.length() > 0); } @Override public void setHeight(String height) { // Override PopupPanel which sets the height to the contents getElement().getStyle().setProperty("height", height); // Update v-has-height in case undefined window is resized setStyleName("v-has-height", height != null && height.length() > 0); } private void onDragEvent(Event event) { if (!WidgetUtil.isTouchEventOrLeftMouseButton(event)) { return; } switch (DOM.eventGetType(event)) { case Event.ONTOUCHSTART: if (event.getTouches().length() > 1) { return; } case Event.ONMOUSEDOWN: beginMovingWindow(event); break; case Event.ONMOUSEUP: case Event.ONTOUCHEND: case Event.ONTOUCHCANCEL: case Event.ONLOSECAPTURE: stopMovingWindow(); break; case Event.ONMOUSEMOVE: case Event.ONTOUCHMOVE: moveWindow(event); break; default: break; } } private void moveWindow(Event event) { if (dragging) { if (cursorInsideBrowserContentArea(event)) { // Only drag while cursor is inside the browser client area final int x = WidgetUtil.getTouchOrMouseClientX(event) - startX + origX; final int y = WidgetUtil.getTouchOrMouseClientY(event) - startY + origY; setPopupPosition(x, y); } event.preventDefault(); } } private void beginMovingWindow(Event event) { if (draggable) { showDraggingCurtain(); dragging = true; startX = WidgetUtil.getTouchOrMouseClientX(event); startY = WidgetUtil.getTouchOrMouseClientY(event); origX = getElement().getAbsoluteLeft(); origY = getElement().getAbsoluteTop(); DOM.setCapture(getElement()); event.preventDefault(); } } private void stopMovingWindow() { dragging = false; hideDraggingCurtain(); DOM.releaseCapture(getElement()); } @Override protected void onPreviewNativeEvent(Event.NativePreviewEvent event) { if (dragging) { Event e = Event.as(event.getNativeEvent()); onDragEvent(e); event.cancel(); } } @Override public void addStyleDependentName(String styleSuffix) { // VWindow's getStyleElement() does not return the same element as // getElement(), so we need to override this. setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix, true); } @Override public void onKeyDown(KeyDownEvent event) { if (vaadinModality && event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE && !isFocusedElementEditable()) { event.preventDefault(); } } @Override public void onKeyUp(KeyUpEvent event) { if (isClosable() && event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) { closeWindow(); } } @Override public void focus() { contentPanel.focus(); } /** * Registers the handlers that prevent to leave the window using the * Tab-key. * <p> * The value of the parameter doTabStop is stored and used for non-modal * windows. For modal windows, the handlers are always registered, while * preserving the stored value. * * @param doTabStop * true to prevent leaving the window, false to allow leaving the * window for non modal windows */ public void setTabStopEnabled(boolean doTabStop) { this.doTabStop = doTabStop; if (doTabStop || vaadinModality) { addTabBlockHandlers(); } else { removeTabBlockHandlers(); } } public void setCloseListener(CloseListener closeListener) { this.closeListener = closeListener; } public interface CloseListener { void onClose(); } }