/*
 * 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.
 */
package gov.noaa.pmel.tmap.las.client.laswidget;

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(0+"px");
	        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;
    }

}