123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 |
- /*
- @ITMillApache2LicenseForJavaFiles@
- */
-
- package com.itmill.toolkit.terminal.gwt.client.ui;
-
- import java.util.Iterator;
- import java.util.Vector;
-
- import com.google.gwt.user.client.Command;
- import com.google.gwt.user.client.DOM;
- import com.google.gwt.user.client.DeferredCommand;
- import com.google.gwt.user.client.Element;
- import com.google.gwt.user.client.Event;
- import com.google.gwt.user.client.Window;
- import com.google.gwt.user.client.ui.Frame;
- import com.google.gwt.user.client.ui.PopupPanel;
- import com.google.gwt.user.client.ui.RootPanel;
- import com.google.gwt.user.client.ui.ScrollListener;
- import com.google.gwt.user.client.ui.ScrollPanel;
- import com.google.gwt.user.client.ui.Widget;
- import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
- import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
- import com.itmill.toolkit.terminal.gwt.client.Paintable;
- import com.itmill.toolkit.terminal.gwt.client.UIDL;
- import com.itmill.toolkit.terminal.gwt.client.Util;
-
- /**
- * "Sub window" component.
- *
- * TODO update position / scrollposition / size to client
- *
- * @author IT Mill Ltd
- */
- public class IWindow extends PopupPanel implements Paintable, ScrollListener {
-
- private static final int MIN_HEIGHT = 60;
-
- private static final int MIN_WIDTH = 80;
-
- private static Vector windowOrder = new Vector();
-
- public static final String CLASSNAME = "i-window";
-
- /**
- * pixels used by inner borders and paddings horizontally (calculated on
- * attach)
- */
- private int borderWidthHorizontal = 0;
-
- private static final int STACKING_OFFSET_PIXELS = 15;
-
- private static final int Z_INDEX_BASE = 10000;
-
- private Paintable layout;
-
- private Element contents;
-
- private Element header;
-
- private Element footer;
-
- private Element resizeBox;
-
- private final ScrollPanel contentPanel = new ScrollPanel();
-
- private boolean dragging;
-
- private int startX;
-
- private int startY;
-
- private int origX;
-
- private int origY;
-
- private boolean resizing;
-
- private int origW;
-
- private int origH;
-
- private Element closeBox;
-
- protected ApplicationConnection client;
-
- private String id;
-
- ShortcutActionHandler shortcutHandler;
-
- /** Last known positionx read from UIDL or updated to application connection */
- private int uidlPositionX = -1;
-
- /** Last known positiony read from UIDL or updated to application connection */
- private int uidlPositionY = -1;
-
- private boolean modal = false;
-
- private boolean resizable = true;
-
- private Element modalityCurtain;
- private Element draggingCurtain;
-
- private Element headerText;
-
- public IWindow() {
- super();
- final int order = windowOrder.size();
- setWindowOrder(order);
- windowOrder.add(this);
- constructDOM();
- setPopupPosition(order * STACKING_OFFSET_PIXELS, order
- * STACKING_OFFSET_PIXELS);
- contentPanel.addScrollListener(this);
- }
-
- private void bringToFront() {
- int curIndex = windowOrder.indexOf(this);
- if (curIndex + 1 < windowOrder.size()) {
- windowOrder.remove(this);
- windowOrder.add(this);
- for (; curIndex < windowOrder.size(); curIndex++) {
- ((IWindow) windowOrder.get(curIndex)).setWindowOrder(curIndex);
- }
- }
- }
-
- /**
- * Returns true if window is the topmost window
- *
- * @return
- */
- private boolean isActive() {
- return windowOrder.lastElement().equals(this);
- }
-
- public void setWindowOrder(int order) {
- int zIndex = (order + Z_INDEX_BASE);
- if (modal) {
- zIndex += 1000;
- DOM.setStyleAttribute(modalityCurtain, "zIndex", "" + zIndex);
- }
- DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex);
- }
-
- protected void constructDOM() {
- setStyleName(CLASSNAME);
-
- header = DOM.createDiv();
- DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
- headerText = DOM.createDiv();
- DOM.setElementProperty(headerText, "className", CLASSNAME + "-header");
- contents = DOM.createDiv();
- DOM.setElementProperty(contents, "className", CLASSNAME + "-contents");
- footer = DOM.createDiv();
- DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
- resizeBox = DOM.createDiv();
- DOM
- .setElementProperty(resizeBox, "className", CLASSNAME
- + "-resizebox");
- closeBox = DOM.createDiv();
- DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
- DOM.appendChild(footer, resizeBox);
-
- DOM.sinkEvents(getElement(), Event.ONLOSECAPTURE);
- DOM.sinkEvents(closeBox, Event.ONCLICK);
- DOM.sinkEvents(contents, Event.ONCLICK);
-
- final Element wrapper = DOM.createDiv();
- DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
-
- final Element wrapper2 = DOM.createDiv();
- DOM.setElementProperty(wrapper2, "className", CLASSNAME + "-wrap2");
-
- DOM.sinkEvents(wrapper, Event.ONKEYDOWN);
-
- DOM.appendChild(wrapper2, closeBox);
- DOM.appendChild(wrapper2, header);
- DOM.appendChild(header, headerText);
- DOM.appendChild(wrapper2, contents);
- DOM.appendChild(wrapper2, footer);
- DOM.appendChild(wrapper, wrapper2);
- DOM.appendChild(super.getContainerElement(), wrapper);
-
- sinkEvents(Event.MOUSEEVENTS);
-
- setWidget(contentPanel);
-
- }
-
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- id = uidl.getId();
- this.client = client;
-
- // Workaround needed for Testing Tools (GWT generates window DOM
- // slightly different in different browsers).
- DOM.setElementProperty(closeBox, "id", id + "_window_close");
-
- if (uidl.hasAttribute("invisible")) {
- this.hide();
- return;
- }
-
- if (client.updateComponent(this, uidl, false)) {
- return;
- }
-
- if (uidl.getBooleanAttribute("modal") != modal) {
- setModal(!modal);
- }
-
- if (uidl.getBooleanAttribute("resizable") != resizable) {
- setResizable(!resizable);
- }
-
- // Initialize the position form UIDL
- try {
- final int positionx = uidl.getIntVariable("positionx");
- final int positiony = uidl.getIntVariable("positiony");
- if (positionx >= 0 && positiony >= 0) {
- setPopupPosition(positionx, positiony);
- }
- } catch (final IllegalArgumentException e) {
- // Silently ignored as positionx and positiony are not required
- // parameters
- }
-
- if (!isAttached()) {
- show();
- }
-
- // Initialize the size from UIDL
- /*
- * FIXME non-pixel size is set as "outer size", pixels are applied for
- * content area. This is due history as earlier only pixels where
- * allowed.
- */
- if (uidl.hasVariable("width")) {
- final String width = uidl.getStringVariable("width");
- if (width.indexOf("px") < 0) {
- /*
- * Only using non-pixel size for initial size measurement. Then
- * fix content area with pixels.
- */
- DOM.setStyleAttribute(getElement(), "width", width);
- int elementPropertyInt = DOM.getElementPropertyInt(
- getElement(), "offsetWidth");
- DOM.setStyleAttribute(getElement(), "width", "");
- elementPropertyInt -= (DOM.getElementPropertyInt(getElement(),
- "offsetWidth") - DOM.getElementPropertyInt(contents,
- "offsetWidth"));
- setWidth(elementPropertyInt + "px");
- } else {
- setWidth(width);
- }
- }
-
- // Height set after show so we can detect space used by decorations
- if (uidl.hasVariable("height")) {
- final String height = uidl.getStringVariable("height");
- if (height.indexOf("%") > 0) {
- int winHeight = Window.getClientHeight();
- float percent = Float.parseFloat(height.substring(0, height
- .indexOf("%"))) / 100.0f;
- int contentPixels = (int) (winHeight * percent);
- contentPixels -= (DOM.getElementPropertyInt(getElement(),
- "offsetHeight") - DOM.getElementPropertyInt(contents,
- "offsetHeight"));
- // FIXME hardcoded contents elements border size
- contentPixels -= 2;
-
- setHeight(contentPixels + "px");
- } else {
- setHeight(height);
- }
- }
-
- if (uidl.hasAttribute("caption")) {
- setCaption(uidl.getStringAttribute("caption"), uidl
- .getStringAttribute("icon"));
- }
-
- boolean showingUrl = false;
- int childIndex = 0;
- UIDL childUidl = uidl.getChildUIDL(childIndex++);
- while ("open".equals(childUidl.getTag())) {
- // TODO multiple opens with the same target will in practice just
- // open the last one - should we fix that somehow?
- final String parsedUri = client.translateToolkitUri(childUidl
- .getStringAttribute("src"));
- if (!childUidl.hasAttribute("name")) {
- final Frame frame = new Frame();
- DOM.setStyleAttribute(frame.getElement(), "width", "100%");
- DOM.setStyleAttribute(frame.getElement(), "height", "100%");
- DOM.setStyleAttribute(frame.getElement(), "border", "0px");
- frame.setUrl(parsedUri);
- contentPanel.setWidget(frame);
- showingUrl = true;
- } else {
- final String target = childUidl.getStringAttribute("name");
- Window.open(parsedUri, target, "");
- }
- childUidl = uidl.getChildUIDL(childIndex++);
- }
-
- final Paintable lo = client.getPaintable(childUidl);
- if (layout != null) {
- if (layout != lo) {
- // remove old
- client.unregisterPaintable(layout);
- contentPanel.remove((Widget) layout);
- // add new
- if (!showingUrl) {
- contentPanel.setWidget((Widget) lo);
- }
- layout = lo;
- }
- } else if (!showingUrl) {
- contentPanel.setWidget((Widget) lo);
- }
- lo.updateFromUIDL(childUidl, client);
-
- // we may have actions and notifications
- if (uidl.getChildCount() > 1) {
- final int cnt = uidl.getChildCount();
- for (int i = 1; i < cnt; i++) {
- childUidl = uidl.getChildUIDL(i);
- if (childUidl.getTag().equals("actions")) {
- if (shortcutHandler == null) {
- shortcutHandler = new ShortcutActionHandler(id, client);
- }
- shortcutHandler.updateActionMap(childUidl);
- } else if (childUidl.getTag().equals("notifications")) {
- // TODO needed? move ->
- for (final Iterator it = childUidl.getChildIterator(); it
- .hasNext();) {
- final UIDL notification = (UIDL) it.next();
- String html = "";
- if (notification.hasAttribute("icon")) {
- final String parsedUri = client
- .translateToolkitUri(notification
- .getStringAttribute("icon"));
- html += "<img src=\"" + parsedUri + "\" />";
- }
- if (notification.hasAttribute("caption")) {
- html += "<h1>"
- + notification
- .getStringAttribute("caption")
- + "</h1>";
- }
- if (notification.hasAttribute("message")) {
- html += "<p>"
- + notification
- .getStringAttribute("message")
- + "</p>";
- }
-
- final String style = notification.hasAttribute("style") ? notification
- .getStringAttribute("style")
- : null;
- final int position = notification
- .getIntAttribute("position");
- final int delay = notification.getIntAttribute("delay");
- new INotification(delay).show(html, position, style);
- }
- }
- }
-
- }
-
- // setting scrollposition must happen after children is rendered
- contentPanel.setScrollPosition(uidl.getIntVariable("scrollTop"));
- contentPanel.setHorizontalScrollPosition(uidl
- .getIntVariable("scrollLeft"));
-
- // Center this window on screen if requested
- // This has to be here because we might not know the content size before
- // everything is painted into the window
- if (uidl.getBooleanAttribute("center")) {
- center();
- }
-
- }
-
- public void show() {
- if (modal) {
- showModalityCurtain();
- }
- super.show();
-
- setFF2CaretFixEnabled(true);
- fixFF3OverflowBug();
- }
-
- /** Disable overflow auto with FF3 to fix #1837. */
- private void fixFF3OverflowBug() {
- if (BrowserInfo.get().isFF3()) {
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- DOM.setStyleAttribute(getElement(), "overflow", "");
- }
- });
- }
- }
-
- /**
- * Fix "missing cursor" browser bug workaround for FF2 in Windows and Linux.
- *
- * Calling this method has no effect on other browsers than the ones based
- * on Gecko 1.8
- *
- * @param enable
- */
- private void setFF2CaretFixEnabled(boolean enable) {
- if (BrowserInfo.get().isFF2()) {
- if (enable) {
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- DOM.setStyleAttribute(getElement(), "overflow", "auto");
- }
- });
- } else {
- DOM.setStyleAttribute(getElement(), "overflow", "");
- }
- }
- }
-
- public void hide() {
- if (modal) {
- hideModalityCurtain();
- }
- super.hide();
- }
-
- private void setModal(boolean modality) {
- modal = modality;
- if (modal) {
- modalityCurtain = DOM.createDiv();
- DOM.setElementProperty(modalityCurtain, "className", CLASSNAME
- + "-modalitycurtain");
- if (isAttached()) {
- showModalityCurtain();
- bringToFront();
- } else {
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- // modal window must on top of others
- bringToFront();
- }
- });
- }
- } else {
- if (modalityCurtain != null) {
- if (isAttached()) {
- hideModalityCurtain();
- }
- modalityCurtain = null;
- }
- }
- }
-
- private void showModalityCurtain() {
- if (BrowserInfo.get().isFF2()) {
- DOM.setStyleAttribute(modalityCurtain, "height", DOM
- .getElementPropertyInt(RootPanel.getBodyElement(),
- "offsetHeight")
- + "px");
- DOM.setStyleAttribute(modalityCurtain, "position", "absolute");
- }
- DOM.appendChild(RootPanel.getBodyElement(), modalityCurtain);
- }
-
- private void hideModalityCurtain() {
- DOM.removeChild(RootPanel.getBodyElement(), modalityCurtain);
- }
-
- /*
- * Shows (or hides) an empty div on top of all other content; used when
- * resizing or moving, so that iframes (etc) do not steal event.
- */
- private void showDraggingCurtain(boolean show) {
- if (show && draggingCurtain == null) {
-
- setFF2CaretFixEnabled(false); // makes FF2 slow
-
- draggingCurtain = DOM.createDiv();
- DOM.setStyleAttribute(draggingCurtain, "position", "absolute");
- DOM.setStyleAttribute(draggingCurtain, "top", "0px");
- DOM.setStyleAttribute(draggingCurtain, "left", "0px");
- DOM.setStyleAttribute(draggingCurtain, "width", "100%");
- DOM.setStyleAttribute(draggingCurtain, "height", "100%");
- DOM.setStyleAttribute(draggingCurtain, "zIndex", ""
- + IToolkitOverlay.Z_INDEX);
-
- DOM.appendChild(RootPanel.getBodyElement(), draggingCurtain);
- } else if (!show && draggingCurtain != null) {
-
- setFF2CaretFixEnabled(true); // makes FF2 slow
-
- DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain);
- draggingCurtain = null;
- }
-
- }
-
- private void setResizable(boolean resizability) {
- resizable = resizability;
- if (resizability) {
- DOM.setElementProperty(resizeBox, "className", CLASSNAME
- + "-resizebox");
- } else {
- DOM.setElementProperty(resizeBox, "className", CLASSNAME
- + "-resizebox " + CLASSNAME + "-resizebox-disabled");
- }
- }
-
- public void setPopupPosition(int left, int top) {
- super.setPopupPosition(left, top);
- if (left != uidlPositionX && client != null) {
- client.updateVariable(id, "positionx", left, false);
- uidlPositionX = left;
- }
- if (top != uidlPositionY && client != null) {
- client.updateVariable(id, "positiony", top, false);
- uidlPositionY = top;
- }
- }
-
- public void setCaption(String c) {
- setCaption(c, null);
- }
-
- public void setCaption(String c, String icon) {
- String html = Util.escapeHTML(c);
- if (icon != null) {
- icon = client.translateToolkitUri(icon);
- html = "<img src=\"" + icon + "\" class=\"i-icon\" />" + html;
- }
- DOM.setInnerHTML(headerText, html);
- }
-
- protected Element getContainerElement() {
- // in GWT 1.5 this method is used in PopupPanel constructor
- if (contents == null) {
- return super.getContainerElement();
- }
- return contents;
- }
-
- public void onBrowserEvent(final Event event) {
- final int type = DOM.eventGetType(event);
-
- if (type == Event.ONKEYDOWN && shortcutHandler != null) {
- shortcutHandler.handleKeyboardEvent(event);
- return;
- }
-
- final Element target = DOM.eventGetTarget(event);
-
- // Handle window caption tooltips
- if (client != null && DOM.isOrHasChild(header, target)) {
- client.handleTooltipEvent(event, this);
- }
-
- if (resizing || DOM.compare(resizeBox, target)) {
- onResizeEvent(event);
- DOM.eventCancelBubble(event, true);
- } else if (DOM.compare(target, closeBox)) {
- if (type == Event.ONCLICK) {
- onCloseClick();
- DOM.eventCancelBubble(event, true);
- }
- } else if (dragging || !DOM.isOrHasChild(contents, target)) {
- onDragEvent(event);
- DOM.eventCancelBubble(event, true);
- } else if (type == Event.ONCLICK) {
- // clicked inside window, ensure to be on top
- if (!isActive()) {
- bringToFront();
- }
- }
- }
-
- private void onCloseClick() {
- client.updateVariable(id, "close", true, true);
- }
-
- private void onResizeEvent(Event event) {
- if (resizable) {
- switch (DOM.eventGetType(event)) {
- case Event.ONMOUSEDOWN:
- if (!isActive()) {
- bringToFront();
- }
- showDraggingCurtain(true);
- resizing = true;
- startX = DOM.eventGetScreenX(event);
- startY = DOM.eventGetScreenY(event);
- origW = getWidget().getOffsetWidth();
- origH = getWidget().getOffsetHeight();
- DOM.setCapture(getElement());
- DOM.eventPreventDefault(event);
- break;
- case Event.ONMOUSEUP:
- showDraggingCurtain(false);
- resizing = false;
- DOM.releaseCapture(getElement());
- setSize(event, true);
- break;
- case Event.ONLOSECAPTURE:
- showDraggingCurtain(false);
- resizing = false;
- case Event.ONMOUSEMOVE:
- if (resizing) {
- setSize(event, false);
- DOM.eventPreventDefault(event);
- }
- break;
- default:
- DOM.eventPreventDefault(event);
- break;
- }
- }
- }
-
- public void setSize(Event event, boolean updateVariables) {
- int w = DOM.eventGetScreenX(event) - startX + origW;
- if (w < MIN_WIDTH) {
- w = MIN_WIDTH;
- }
- int h = DOM.eventGetScreenY(event) - startY + origH;
- if (h < MIN_HEIGHT) {
- h = MIN_HEIGHT;
- }
- setWidth(w + "px");
- setHeight(h + "px");
- if (updateVariables) {
- // sending width back always as pixels, no need for unit
- client.updateVariable(id, "width", w, false);
- client.updateVariable(id, "height", h, false);
- }
- // Update child widget dimensions
- Util.runDescendentsLayout(this);
- }
-
- public void setWidth(String width) {
- if (!"".equals(width)) {
- DOM
- .setStyleAttribute(
- getElement(),
- "width",
- (Integer.parseInt(width.substring(0,
- width.length() - 2)) + borderWidthHorizontal)
- + "px");
- }
- }
-
- private void onDragEvent(Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.ONMOUSEDOWN:
- if (!isActive()) {
- bringToFront();
- }
- showDraggingCurtain(true);
- dragging = true;
- startX = DOM.eventGetScreenX(event);
- startY = DOM.eventGetScreenY(event);
- origX = DOM.getAbsoluteLeft(getElement());
- origY = DOM.getAbsoluteTop(getElement());
- DOM.setCapture(getElement());
- DOM.eventPreventDefault(event);
- break;
- case Event.ONMOUSEUP:
- dragging = false;
- showDraggingCurtain(false);
- DOM.releaseCapture(getElement());
- break;
- case Event.ONLOSECAPTURE:
- showDraggingCurtain(false);
- dragging = false;
- break;
- case Event.ONMOUSEMOVE:
- if (dragging) {
- final int x = DOM.eventGetScreenX(event) - startX + origX;
- final int y = DOM.eventGetScreenY(event) - startY + origY;
- setPopupPosition(x, y);
- DOM.eventPreventDefault(event);
- }
- break;
- default:
- break;
- }
- }
-
- public boolean onEventPreview(Event event) {
- if (dragging) {
- onDragEvent(event);
- return false;
- } else if (resizing) {
- onResizeEvent(event);
- return false;
- } else if (modal) {
- // return false when modal and outside window
- final Element target = DOM.eventGetTarget(event);
- if (!DOM.isOrHasChild(getElement(), target)) {
- return false;
- }
- }
- return true;
- }
-
- public void onScroll(Widget widget, int scrollLeft, int scrollTop) {
- client.updateVariable(id, "scrollTop", scrollTop, false);
- client.updateVariable(id, "scrollLeft", scrollLeft, false);
- }
-
- public void addStyleDependentName(String styleSuffix) {
- // IWindow's getStyleElement() does not return the same element as
- // getElement(), so we need to override this.
- setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix,
- true);
- }
-
- protected void onAttach() {
- super.onAttach();
- // Calculate space required by window borders, so we can accurately
- // calculate space for content
- final int contentWidth = DOM.getElementPropertyInt(contentPanel
- .getElement(), "offsetWidth");
- final int windowWidth = DOM.getElementPropertyInt(getElement(),
- "offsetWidth");
- borderWidthHorizontal = windowWidth - contentWidth;
- }
-
- }
|