package com.itmill.toolkit.terminal.gwt.client.ui; import java.util.Vector; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.KeyboardListenerCollection; import com.google.gwt.user.client.ui.PopupPanel; 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.Paintable; import com.itmill.toolkit.terminal.gwt.client.UIDL; /** * "Sub window" component. * * TODO update position / scrollposition / size to client * * @author IT Mill Ltd */ public class IWindow extends PopupPanel implements Paintable, ScrollListener { private static Vector windowOrder = new Vector(); public static final String CLASSNAME = "i-window"; /** pixels used by inner borders and paddings horizontally */ protected static final int BORDER_WIDTH_HORIZONTAL = 0; /** pixels used by headers, footers, inner borders and paddings vertically */ protected static final int BORDER_WIDTH_VERTICAL = 22; 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 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; private ApplicationConnection client; private String id; ShortcutActionHandler shortcutHandler; /** Last known width read from UIDL or updated to application connection */ private int uidlWidth = -1; /** Last known height read from UIDL or updated to application connection */ private int uidlHeight = -1; /** 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; public IWindow() { super(); int order = windowOrder.size(); setWindowOrder(order); windowOrder.add(this); setStyleName(CLASSNAME); 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) { DOM.setStyleAttribute(getElement(), "zIndex", "" + (order + Z_INDEX_BASE)); } protected void constructDOM() { header = DOM.createDiv(); DOM.setElementProperty(header, "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(header, Event.MOUSEEVENTS); DOM.sinkEvents(resizeBox, Event.MOUSEEVENTS); DOM.sinkEvents(closeBox, Event.ONCLICK); DOM.sinkEvents(contents, Event.ONCLICK); Element wrapper = getElement(); DOM.sinkEvents(wrapper, Event.ONKEYDOWN); DOM.appendChild(wrapper, closeBox); DOM.appendChild(wrapper, header); DOM.appendChild(wrapper, contents); DOM.appendChild(wrapper, footer); setWidget(contentPanel); // set default size setWidth(400 + "px"); setHeight(300 + "px"); } public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { this.id = uidl.getId(); this.client = client; if (uidl.getBooleanAttribute("cached")) return; if (uidl.hasAttribute("invisible")) { this.hide(); return; } else { // Initialize the width from UIDL if (uidl.hasVariable("width")) { String width = uidl.getStringVariable("width"); setWidth(width); } if (uidl.hasVariable("height")) { String height = uidl.getStringVariable("height"); setHeight(height); } contentPanel.setScrollPosition(uidl.getIntVariable("scrolltop")); contentPanel.setHorizontalScrollPosition(uidl .getIntVariable("scrollleft")); // Initialize the position form UIDL try { int positionx = uidl.getIntVariable("positionx"); int positiony = uidl.getIntVariable("positiony"); if (positionx >= 0 && positiony >= 0) { setPopupPosition(positionx, positiony); } } catch (IllegalArgumentException e) { // Silently ignored as positionx and positiony are not required // parameters } if (!isAttached()) { show(); } } UIDL childUidl = uidl.getChildUIDL(0); Paintable lo = (Paintable) client.getWidget(childUidl); if (layout != null) { if (layout != lo) { // remove old client.unregisterPaintable(layout); contentPanel.remove((Widget) layout); // add new contentPanel.setWidget((Widget) lo); layout = lo; } } else { contentPanel.setWidget((Widget) lo); } if (uidl.hasAttribute("caption")) { setCaption(uidl.getStringAttribute("caption")); } lo.updateFromUIDL(childUidl, client); // we may have actions if (uidl.getChidlCount() > 1) { childUidl = uidl.getChildUIDL(1); if (childUidl.getTag().equals("actions")) { if (shortcutHandler == null) shortcutHandler = new ShortcutActionHandler(id, client); shortcutHandler.updateActionMap(childUidl); } } } 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) { DOM.setInnerHTML(header, c); } protected Element getContainerElement() { return contents; } public void onBrowserEvent(Event event) { int type = DOM.eventGetType(event); if (type == Event.ONKEYDOWN && shortcutHandler != null) { int modifiers = KeyboardListenerCollection .getKeyboardModifiers(event); shortcutHandler.handleKeyboardEvent((char) DOM .eventGetKeyCode(event), modifiers); return; } if (!isActive()) { bringToFront(); } Element target = DOM.eventGetTarget(event); if (dragging || DOM.compare(header, target)) onHeaderEvent(event); else if (resizing || DOM.compare(resizeBox, target)) onResizeEvent(event); else if (DOM.compare(target, closeBox) && type == Event.ONCLICK) { onCloseClick(); } } private void onCloseClick() { client.updateVariable(id, "close", true, true); } private void onResizeEvent(Event event) { switch (DOM.eventGetType(event)) { case Event.ONMOUSEDOWN: resizing = true; startX = DOM.eventGetScreenX(event); startY = DOM.eventGetScreenY(event); origW = getOffsetWidth(); origH = getOffsetHeight(); DOM.addEventPreview(this); break; case Event.ONMOUSEUP: resizing = false; DOM.removeEventPreview(this); setSize(event, true); break; case Event.ONMOUSEMOVE: if (resizing) { setSize(event, false); DOM.eventPreventDefault(event); } break; default: break; } } public void setSize(Event event, boolean updateVariables) { int w = DOM.eventGetScreenX(event) - startX + origW; if (w < 60) w = 60; int h = DOM.eventGetScreenY(event) - startY + origH; if (h < 60) h = 60; 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); } } public void setWidth(String width) { super.setWidth(width); DOM.setStyleAttribute(header, "width", width); } private void onHeaderEvent(Event event) { switch (DOM.eventGetType(event)) { case Event.ONMOUSEDOWN: dragging = true; startX = DOM.eventGetScreenX(event); startY = DOM.eventGetScreenY(event); origX = DOM.getAbsoluteLeft(getElement()); origY = DOM.getAbsoluteTop(getElement()); DOM.addEventPreview(this); break; case Event.ONMOUSEUP: dragging = false; DOM.removeEventPreview(this); break; case Event.ONMOUSEMOVE: if (dragging) { int x = DOM.eventGetScreenX(event) - startX + origX; int y = DOM.eventGetScreenY(event) - startY + origY; this.setPopupPosition(x, y); DOM.eventPreventDefault(event); } break; default: break; } } public boolean onEventPreview(Event event) { if (dragging) { onHeaderEvent(event); return false; } else if (resizing) { onResizeEvent(event); return false; } // TODO return false when modal return true; } public void onScroll(Widget widget, int scrollLeft, int scrollTop) { client.updateVariable(id, "scrolltop", scrollTop, false); client.updateVariable(id, "scrollleft", scrollLeft, false); } }