Java tutorial
package com.googlecode.kanbanik.client.components; /* * Copyright 2010 Traction Software, Inc. * Copyright 2010 clazzes.org Project * * Based on TractionDialogBox by Traction Software, Inc. Renamed to WindowBox and * added resize support by clazzes.org * * 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. */ import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style.Cursor; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DomEvent; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.logical.shared.HasOpenHandlers; import com.google.gwt.event.logical.shared.OpenEvent; import com.google.gwt.event.logical.shared.OpenHandler; import com.google.gwt.event.shared.HandlerRegistration; 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.Event.NativePreviewEvent; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; /** * Extension of the standard GWT DialogBox to provide a more "window"-like functionality. By default, the WindowBox * has two control-buttons in the top right corner of the header, which allow the box to be reduced to it's header * ("minimize") or the whole box to be hidden ("close"). The visiblity of these controls can be toggled seperately * with {@link #setMinimizeIconVisible(boolean)} and {@link #setCloseIconVisible(boolean)} respectively. * <br><br> * The WindowBox relies on the css settings of {@link DialogBox} for styling of the border and header. It also uses * the following classes to style the additional elements: * * <pre> * .gwt-extras-WindowBox * the box itself * .gwt-extras-WindowBox .gwt-extras-dialog-container * the div holding the contents of the box * .gwt-extras-WindowBox .gwt-extras-dialog-controls * the div holding the window-controls - PLEASE NOTE: on the DOM-tree, this div is located inside the center-center * cell of the windowBox table, not in the top-center (where the header-text is). Therefore the css has a negative * top-value to position the controls on the header * .gwt-extras-WindowBox .gwt-extras-dialog-controls a.gwt-extras-dialog-close * .gwt-extras-WindowBox .gwt-extras-dialog-controls a.gwt-extras-dialog-close:hover * .gwt-extras-WindowBox .gwt-extras-dialog-controls a.gwt-extras-dialog-minimize * .gwt-extras-WindowBox .gwt-extras-dialog-controls a.gwt-extras-dialog-minimize:hover * .gwt-extras-WindowBox .gwt-extras-dialog-controls a.gwt-extras-dialog-maximize * .gwt-extras-WindowBox .gwt-extras-dialog-controls a.gwt-extras-dialog-maximize:hover * the controls in the header. A background image sprite is used to create the mouseover- and clicking-effects. * When the window is minimized, the style-name of the corresponding control changes to "gwt-extras-dialog-maximize" * and vice-versa * </pre> */ public class WindowBox extends DialogBox implements HasOpenHandlers<WindowBox> { private static final int MIN_WIDTH = 100; private static final int MIN_HEIGHT = 100; private FlowPanel container; //private FlowPanel content; private FlowPanel controls; private Anchor close; private Anchor minimize; private int dragX; private int dragY; private int minWidth = MIN_WIDTH; private int minHeight = MIN_HEIGHT; private int dragMode; private boolean resizable; private boolean minimized; /** * Static helper method to change the cursor for a given element when resizing is enabled. * * @param dm The code describing the position of the element in question * @param element The {@link com.google.gwt.dom.client.Element} to set the cursor on */ protected static void updateCursor(int dm, com.google.gwt.dom.client.Element element) { Cursor cursor; switch (dm) { case 0: cursor = Cursor.NW_RESIZE; break; case 1: cursor = Cursor.N_RESIZE; break; case 2: cursor = Cursor.NE_RESIZE; break; case 3: cursor = Cursor.W_RESIZE; break; case 5: cursor = Cursor.E_RESIZE; break; case 6: cursor = Cursor.SW_RESIZE; break; case 7: cursor = Cursor.S_RESIZE; break; case 8: cursor = Cursor.SE_RESIZE; break; default: cursor = Cursor.AUTO; break; } element.getStyle().setCursor(cursor); } /** * Creates a DialogBoxEx which is permanent (no auto-hide), non-modal, has a "minimize"- and "close"-button * in the top-right corner and is not resizeable. The dialog box should not be shown until a child widget has been * added using {@link #add(com.google.gwt.user.client.ui.IsWidget)}. * <br><br> * This is the equivalent for calling <code>DialogBoxEx(false, false, true, false)</code>. * * @see WindowBox#DialogBoxEx(boolean, boolean, boolean, boolean) */ public WindowBox() { this(false, false, true, true, false); } /** * Creates a DialogBoxEx which is permanent, non-modal, has a "minimize"- and "close"-button and is optionally resizeable. * The dialog box should not be shown until a child widget has been added using * {@link #setWidget(Widget)}. * * @see WindowBox#DialogBoxEx(boolean, boolean, boolean, boolean) * * @param resizeable <code>true</code> to allow resizing by dragging the borders */ public WindowBox(boolean resizeable) { this(false, false, true, true, resizeable); } /** * Creates a DialogBoxEx which is permanent and nonmodal, optionally resizeable and/or has a "minimize"- and * "close"-button. The dialog box should not be shown until a child widget has been added using * {@link #setWidget(Widget)}. * * @see WindowBox#DialogBoxEx(boolean, boolean, boolean, boolean) * * @param resizeable <code>true</code> to allow resizing by dragging the borders * @param showCloseIcon <code>true</code> to show "close"-icon in the top right corner of the header */ public WindowBox(boolean showCloseIcon, boolean resizeable) { this(false, false, true, showCloseIcon, resizeable); } /** * Creates a DialogBoxEx which is permanent and nonmodal, optionally resizeable and/or has a "minimize"- and * "close"-button. The dialog box should not be shown until a child widget has been added using * {@link #setWidget(Widget)}. * * @see WindowBox#DialogBoxEx(boolean, boolean, boolean, boolean) * * @param showMinimizeIcon <code>true</code> to show "minimize"-icon int the top right corner of the header * @param resizeable <code>true</code> to allow resizing by dragging the borders * @param showCloseIcon <code>true</code> to show "close"-icon in the top right corner of the header */ public WindowBox(boolean showMinimizeIcon, boolean showCloseIcon, boolean resizeable) { this(false, false, showMinimizeIcon, showCloseIcon, resizeable); } /** * Creates a DialogBoxEx which is permanent, optionally modal, resizeable and/or has a "minimize"- and * "close"-button. The dialog box should not be shown until a child widget has been added using * {@link #setWidget(Widget)}. * * @param modal <code>true</code> if keyboard and mouse events for widgets not contained by the dialog * should be ignored * @param showMinimizeIcon <code>true</code> to show "minimize"-icon int the top right corner of the header * @param resizeable <code>true</code> to allow resizing by dragging the borders * @param showCloseIcon <code>true</code> to show "close"-icon in the top right corner of the header */ public WindowBox(boolean modal, boolean showMinimizeIcon, boolean showCloseIcon, boolean resizeable) { this(false, modal, showMinimizeIcon, showCloseIcon, resizeable); } /** * Creates an empty DialogBoxEx with all configuration options. * The dialog box should not be shown until a child widget has been added using * {@link #setWidget(Widget)}. * * @see DialogBox#DialogBox() * @see DialogBox#DialogBox(boolean) * @see DialogBox#DialogBox(boolean, boolean) * @see DialogBox#DialogBox(boolean, boolean, boolean) * * @param autoHide <code>true</code> if the dialog should be automatically hidden when the user clicks outside of it * @param modal <code>true</code> if keyboard and mouse events for widgets not contained by the dialog * should be ignored * @param showMinimizeIcon <code>true</code> to show "minimize"-icon int the top right corner of the header * @param showCloseIcon <code>true</code> to show "close"-icon in the top right corner of the header * @param resizeable <code>true</code> to allow resizing by dragging the borders */ public WindowBox(boolean autoHide, boolean modal, boolean showMinimizeIcon, boolean showCloseIcon, boolean resizeable) { super(autoHide, modal); this.setStyleName("gwt-extras-WindowBox", true); this.container = new FlowPanel(); this.container.addStyleName("gwt-extras-dialog-container"); //this.content = new FlowPanel(); this.close = new Anchor(); this.close.setStyleName("gwt-extras-dialog-close"); this.close.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { onCloseClick(event); } }); setCloseIconVisible(showCloseIcon); this.minimize = new Anchor(); this.minimize.setStyleName("gwt-extras-dialog-minimize"); this.minimize.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { onMinimizeClick(event); } }); setMinimizeIconVisible(showMinimizeIcon); Grid ctrlGrid = new Grid(1, 2); ctrlGrid.setWidget(0, 0, this.minimize); ctrlGrid.setWidget(0, 1, this.close); this.controls = new FlowPanel(); this.controls.setStyleName("gwt-extras-dialog-controls"); this.controls.add(ctrlGrid); this.dragMode = -1; this.resizable = resizeable; } /** * Sets the cursor to indicate resizability for a specified "drag-mode" (i.e. how the box is being resized) * on the dialog box. The position is described by an integer, as follows: * * <pre> * 0-- --1-- --2 * | | * * | | * 3 -1 5 * | | * * | | * 6-- --7-- --8 * </pre> * * passing <code>-1</code> resets the cursor to the default. * @param dragMode */ protected void updateCursor(int dragMode) { if (this.resizable) { updateCursor(dragMode, this.getElement()); com.google.gwt.dom.client.Element top = this.getCellElement(0, 1); updateCursor(dragMode, top); top = Element.as(top.getFirstChild()); if (top != null) updateCursor(dragMode, top); } } /** * Returns whether the dialog box is mouse-resizeable * * @return <code>true</code> if the user can resize the dialog with the mouse */ public boolean isResizable() { return this.resizable; } /** * Set the dialog box to be resizeable by the user * * @param resizable <code>true</code> if the user can resize the dialog with the mouse */ public void setResizable(boolean resizable) { this.resizable = resizable; } /* * (non-Javadoc) * @see com.google.gwt.user.client.ui.DialogBox#onBrowserEvent(com.google.gwt.user.client.Event) */ @Override public void onBrowserEvent(Event event) { // If we're not yet dragging, only trigger mouse events if the event occurs // in the caption wrapper if (this.resizable) { switch (event.getTypeInt()) { case Event.ONMOUSEDOWN: case Event.ONMOUSEUP: case Event.ONMOUSEMOVE: case Event.ONMOUSEOVER: case Event.ONMOUSEOUT: if (this.dragMode >= 0 || calcDragMode(event.getClientX(), event.getClientY()) >= 0) { // paste'n'copy from Widget.onBrowserEvent switch (DOM.eventGetType(event)) { case Event.ONMOUSEOVER: // Only fire the mouse over event if it's coming from outside this // widget. case Event.ONMOUSEOUT: // Only fire the mouse out event if it's leaving this // widget. Element related = event.getRelatedEventTarget().cast(); if (related != null && getElement().isOrHasChild(related)) { return; } break; } DomEvent.fireNativeEvent(event, this, this.getElement()); return; } if (this.dragMode < 0) this.updateCursor(this.dragMode); } } super.onBrowserEvent(event); } /** * * @param resize * @param clientX * @return */ private int getRelX(com.google.gwt.dom.client.Element resize, int clientX) { return clientX - resize.getAbsoluteLeft() + resize.getScrollLeft() + resize.getOwnerDocument().getScrollLeft(); } /** * * @param resize * @param clientY * @return */ private int getRelY(com.google.gwt.dom.client.Element resize, int clientY) { return clientY - resize.getAbsoluteTop() + resize.getScrollTop() + resize.getOwnerDocument().getScrollTop(); } /** * Calculates the position of the mouse relative to the dialog box, and returns the corresponding "drag-mode" * integer, which describes which area of the box is being resized. * * @param clientX The x-coordinate of the mouse in screen pixels * @param clientY The y-coordinate of the mouse in screen pixels * @return A value in range [-1..8] describing the position of the mouse (see {@link #updateCursor(int)} for more * information) */ protected int calcDragMode(int clientX, int clientY) { com.google.gwt.dom.client.Element resize = this.getCellElement(2, 2).getParentElement(); int xr = this.getRelX(resize, clientX); int yr = this.getRelY(resize, clientY); int w = resize.getClientWidth(); int h = resize.getClientHeight(); if ((xr >= 0 && xr < w && yr >= -5 && yr < h) || (yr >= 0 && yr < h && xr >= -5 && xr < w)) return 8; resize = this.getCellElement(2, 0).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if ((xr >= 0 && xr < w && yr >= -5 && yr < h) || (yr >= 0 && yr < h && xr >= 0 && xr < w + 5)) return 6; resize = this.getCellElement(0, 2).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if ((xr >= 0 && xr < w && yr >= 0 && yr < h + 5) || (yr >= 0 && yr < h && xr >= -5 && xr < w)) return 2; resize = this.getCellElement(0, 0).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if ((xr >= 0 && xr < w && yr >= 0 && yr < h + 5) || (yr >= 0 && yr < h && xr >= 0 && xr < w + 5)) return 0; resize = this.getCellElement(0, 1).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if (yr >= 0 && yr < h) return 1; resize = this.getCellElement(1, 0).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if (xr >= 0 && xr < w) return 3; resize = this.getCellElement(2, 1).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if (yr >= 0 && yr < h) return 7; resize = this.getCellElement(1, 2).getParentElement(); xr = this.getRelX(resize, clientX); yr = this.getRelY(resize, clientY); if (xr >= 0 && xr < w) return 5; return -1; } /** * Convenience method to set the height, width and position of the given widget * * @param panel * @param dx * @param dy */ protected void dragResizeWidget(PopupPanel panel, int dx, int dy) { int x = this.getPopupLeft(); int y = this.getPopupTop(); Widget widget = panel.getWidget(); // left + right if ((this.dragMode % 3) != 1) { int w = widget.getOffsetWidth(); // left edge -> move left if ((this.dragMode % 3) == 0) { x += dx; w -= dx; } else { w += dx; } w = w < this.minWidth ? this.minWidth : w; widget.setWidth(w + "px"); } // up + down if ((this.dragMode / 3) != 1) { int h = widget.getOffsetHeight(); // up = dy is negative if ((this.dragMode / 3) == 0) { y += dy; h -= dy; } else { h += dy; } h = h < this.minHeight ? this.minHeight : h; widget.setHeight(h + "px"); } if (this.dragMode / 3 == 0 || this.dragMode % 3 == 0) panel.setPopupPosition(x, y); } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.DialogBox#beginDragging(com.google.gwt.event.dom.client.MouseDownEvent) */ @Override protected void beginDragging(MouseDownEvent event) { int dm = -1; if (this.resizable && !this.minimized) dm = this.calcDragMode(event.getClientX(), event.getClientY()); if (this.resizable && dm >= 0) { this.dragMode = dm; DOM.setCapture(getElement()); this.dragX = event.getClientX(); this.dragY = event.getClientY(); updateCursor(dm, RootPanel.get().getElement()); } else { super.beginDragging(event); } } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.DialogBox#continueDragging(com.google.gwt.event.dom.client.MouseMoveEvent) */ @Override protected void continueDragging(MouseMoveEvent event) { if (this.dragMode >= 0 && this.resizable) { this.updateCursor(this.dragMode); int dx = event.getClientX() - this.dragX; int dy = event.getClientY() - this.dragY; this.dragX = event.getClientX(); this.dragY = event.getClientY(); dragResizeWidget(this, dx, dy); } else { // this updates the cursor when dragging is NOT activated if (!this.minimized) { int dm = calcDragMode(event.getClientX(), event.getClientY()); this.updateCursor(dm); } super.continueDragging(event); } } /* * (non-Javadoc) * @see com.google.gwt.user.client.ui.DialogBox#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent) */ @Override protected void onPreviewNativeEvent(NativePreviewEvent event) { if (this.resizable) { // We need to preventDefault() on mouseDown events (outside of the // DialogBox content) to keep text from being selected when it // is dragged. NativeEvent nativeEvent = event.getNativeEvent(); if (!event.isCanceled() && (event.getTypeInt() == Event.ONMOUSEDOWN) && calcDragMode(nativeEvent.getClientX(), nativeEvent.getClientY()) >= 0) { nativeEvent.preventDefault(); } } super.onPreviewNativeEvent(event); } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.DialogBox#endDragging(com.google.gwt.event.dom.client.MouseUpEvent) */ @Override protected void endDragging(MouseUpEvent event) { if (this.dragMode >= 0 && this.resizable) { DOM.releaseCapture(getElement()); this.dragX = event.getClientX() - this.dragX; this.dragY = event.getClientY() - this.dragY; this.dragMode = -1; this.updateCursor(this.dragMode); RootPanel.get().getElement().getStyle().setCursor(Cursor.AUTO); } else { super.endDragging(event); } } /* * (non-Javadoc) * @see com.google.gwt.user.client.ui.DecoratedPopupPanel#setWidget(com.google.gwt.user.client.ui.Widget) */ @Override public void setWidget(Widget widget) { if (this.container.getWidgetCount() == 0) { // setup this.container.add(this.controls); //this.container.add(this.content); super.setWidget(this.container); } else { // remove the old one this.container.remove(1); } this.container.add(widget); // add the new widget // this.content.add(widget); } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.DecoratedPopupPanel#getWidget() */ @Override public Widget getWidget() { if (this.container.getWidgetCount() > 1) return this.container.getWidget(1); else return null; } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.DecoratedPopupPanel#remove(com.google.gwt.user.client.ui.Widget) */ @Override public boolean remove(Widget w) { return this.container.remove(w); } /** * Set whether the "close"-button should appear in the top right corner of the header * * @param visible <code>true</code> if a "close"-button should be shown */ public void setCloseIconVisible(boolean visible) { this.close.setVisible(visible); } /** * Set whether the "minimize"-button should appear in the top right corner of the header * * @param visible <code>true</code> if a "minimize"-button should be shown */ public void setMinimizeIconVisible(boolean visible) { this.minimize.setVisible(visible); } /** * Returns the FlowPanel that contains the controls. More controls can be * added directly to this. */ public FlowPanel getControlPanel() { return this.controls; } /** * Called when the close icon is clicked. The default implementation hides the dialog box. * * @param event The {@link ClickEvent} to handle */ protected void onCloseClick(ClickEvent event) { hide(); } /** * Called when the minimize icon is clicked. The default implementation hides the container of the dialog box. * * @param event The {@link ClickEvent} to handle */ protected void onMinimizeClick(ClickEvent event) { Widget widget = getWidget(); if (widget == null) return; boolean visible = widget.isVisible(); int offsetWidth = widget.getOffsetWidth(); widget.setVisible(!visible); this.minimized = visible; if (visible) { this.container.setWidth(offsetWidth + "px"); this.minimize.setStyleName("gwt-extras-dialog-maximize"); } else { this.container.setWidth(null); this.minimize.setStyleName("gwt-extras-dialog-minimize"); } } /* * (non-Javadoc) * @see com.google.gwt.event.logical.shared.HasOpenHandlers#addOpenHandler(com.google.gwt.event.logical.shared.OpenHandler) */ @Override public HandlerRegistration addOpenHandler(OpenHandler<WindowBox> handler) { return addHandler(handler, OpenEvent.getType()); } /* * (non-Javadoc) * @see com.google.gwt.user.client.ui.DialogBox#show() */ @Override public void show() { boolean fireOpen = !isShowing(); super.show(); if (fireOpen) { OpenEvent.fire(this, this); } } /** * Sets the minimum width to which this widget can be resized by the user, if resizing is enabled. If the value * is invalid, it is reset to {@link #MIN_WIDTH} * * @param minWidth A positive int value */ public void setMinWidth(int minWidth) { if (minWidth < 1) minWidth = MIN_WIDTH; this.minWidth = minWidth; } /** * Sets the minimum height to which this widget can be resized by the user, if resizing is enabled. If the value * is invalid, it is reset to {@link #MIN_WIDTH} * * @param minHeight A positive int value */ public void setMinHeight(int minHeight) { if (minHeight < 1) minHeight = MIN_HEIGHT; this.minHeight = minHeight; } }