/*
* MyGWT Widget Library
* Copyright(c) 2007, MyGWT.
* licensing@mygwt.net
*
* http://mygwt.net/license
*/
package net.mygwt.ui.client.widget;
import net.mygwt.ui.client.Events;
import net.mygwt.ui.client.MyDOM;
import net.mygwt.ui.client.MyGWT;
import net.mygwt.ui.client.Style;
import net.mygwt.ui.client.event.BaseEvent;
import net.mygwt.ui.client.event.Listener;
import net.mygwt.ui.client.event.ShellListener;
import net.mygwt.ui.client.event.TypedListener;
import net.mygwt.ui.client.fx.Draggable;
import net.mygwt.ui.client.fx.FXStyle;
import net.mygwt.ui.client.fx.Resizable;
import net.mygwt.ui.client.util.Format;
import net.mygwt.ui.client.util.Markup;
import net.mygwt.ui.client.util.Rectangle;
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.EventPreview;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.WidgetHelper;
/**
* A <code>Shell</code> is a window with a header and content area.
*
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>CLOSE, RESIZE, MODAL, AUTO_HIDE</dd>
* <p>
* <dt><b>Events:</b></dt>
*
* <dd><b>Activate</b> : (widget)<br>
* <div>Fires after a shell is activated (receives focus).</div>
* <ul>
* <li>widget : this</li>
* </ul>
* </dd>
*
* <dd><b>Deactivate</b> : (widget)<br>
* <div>Fires after a shell is deactivated.</div>
* <ul>
* <li>widget : this</li>
* </ul>
* </dd>
*
* <dd><b>Close</b> : (widget)<br>
* <div>Fires after a shell is closed.</div>
* <ul>
* <li>widget : this</li>
* </ul>
* </dd>
*
* <dd><b>BeforeShow</b> : (widget)<br>
* <div>Fires before the shell is opened. Listeners can set the
* <code>doit</code> field to <code>false</code> to cancel the expand.</div>
* <ul>
* <li>widget : this</li>
* </ul>
* </dd>
*
* <dd><b>Show</b> : (widget)<br>
* <div>Fires after the shell is opened.</div>
* <ul>
* <li>widget : this</li>
* </ul>
* </dd>
*
* <dt><b>CSS Styles:</b></dt>
* <dd>.my-shell { the shell itself }</dd>
* <dd>.my-shell-hdr { header }</dd>
* <dd>.my-shell-hdr-text { header text }</dd>
* <dd>.my-shell-body { body }</dd>
* <dd>.my-shell-plain { add for plain content area }</dd>
* </dl>
*/
public class Shell extends Component {
private static FramePanel framePanel = new FramePanel();
/**
* Specifies the minimum width. Default value is 200.
*/
public int minimumWidth = 200;
/**
* Specifies the minimum height. Default value is 100;
*/
public int minimumHeight = 100;
/**
* Specifies the initial width. Default value is 300.
*/
public int initialWidth = 300;
/**
* Specifies the initial height. Default value is 150.
*/
public int initialHeight = 150;
/**
* Specifies if the the open and closing of the shell should be animated.
* Default value is <code>false</code>.
*/
public boolean animate = false;
/**
* Specifies the shadow position. Valid values are NONE, DROP, SIDES, FRAME.
* Default values is SIDES.
*/
public int shadowPosition = Style.SIDES;
/**
* animationDuration specifies the length of the fade effect. Default value is
* 300;
*/
public int animateDuration = 300;
protected int style;
protected Item header;
protected Element bodyElem, contentElem, footerElem;
protected ModalPanel modal;
protected IconButton closeBtn;
protected WidgetContainer content;
protected Shadow shadow;
private Draggable draggable;
private Resizable resizer;
private Rectangle last;
private boolean showing;
private boolean created;
private Listener dragListener;
private EventPreview autoHidePreview;
/**
* Creates a new window that is initialy invisible. A new shell is created
* with default style settings.
* <p>
* Default styles: CLOSE, RESIZE.
* </p>
*/
public Shell() {
this(Style.CLOSE | Style.RESIZE);
}
/**
* Creates a new shell instance.
*
* @param style the style information
*/
public Shell(int style) {
this.style = style;
header = new Item("my-shell-hdr") {
protected void onClick(BaseEvent be) {
super.onClick(be);
activateShell(be.event);
}
};
content = new WidgetContainer();
}
/**
* Adds a listener interface to receive shell events.
*
* @param listener the listener to be added
*/
public void addShellListener(ShellListener listener) {
TypedListener typedListener = new TypedListener(listener);
addListener(Events.Activate, typedListener);
addListener(Events.Deactivate, typedListener);
addListener(Events.Close, typedListener);
}
/**
* Adds a new button to the tool area of the shell.
*
* @param btn the button to add
*/
public void addTool(IconButton btn) {
btn.cancelBubble = true;
btn.addStyleName("my-tool");
header.addWidget(btn);
}
/**
* Closes the shell.
*/
public void close() {
last = getBounds();
if (shadow != null) {
shadow.setVisible(false);
}
if (animate) {
FXStyle fx = new FXStyle(getElement());
fx.duration = animateDuration;
fx.addListener(Events.EffectComplete, new Listener() {
public void handleEvent(BaseEvent be) {
afterHide();
}
});
fx.fadeOut();
} else {
afterHide();
}
ShellManager.get().unregister(this);
}
/**
* Returns the content panel of the shell.
*
* @return the panel
*/
public WidgetContainer getContent() {
return content;
}
/**
* Returns the shell's header text.
*
* @return the text
*/
public String getText() {
return header.getText();
}
public void onBrowserEvent(Event event) {
if (DOM.eventGetType(event) == Event.ONCLICK) {
activateShell(event);
}
}
/**
* Opens the shell.
*/
public void open() {
if (!rendered) {
render();
}
if (showing) {
return;
}
if (!fireEvent(Events.BeforeShow)) {
return;
}
// when removing from panel, position gets removed
setStyleAttribute("position", "absolute");
if (!created) {
createContent(getContent());
created = true;
}
if (modal != null) {
modal.show(this);
} else {
RootPanel.get().add(this);
}
if (MyGWT.isIE && !MyGWT.isIE7) {
framePanel.onShow(getElement());
}
if (last != null) {
MyDOM.setVisible(getElement(), true);
setBounds(last);
} else {
setSize(initialWidth, initialHeight);
if (resizer != null) {
resizer.minHeight = minimumHeight;
resizer.minWidth = minimumWidth;
}
MyDOM.center(getElement());
}
ShellManager.get().register(this);
ShellManager.get().setActive(this);
showing = true;
final Shell fShell = this;
if (animate) {
FXStyle fx = new FXStyle(getElement());
if (shadow != null) {
fx.addListener(Events.EffectComplete, new Listener() {
public void handleEvent(BaseEvent be) {
shadow.show(fShell);
afterShow();
}
});
}
fx.duration = animateDuration;
fx.fadeIn();
} else {
if (shadow != null) {
shadow.setVisible(true);
shadow.show(this);
}
afterShow();
}
}
/**
* Removes a previously added listener interface.
*
* @param listener the listener interface to remove
*/
public void removeShellListener(ShellListener listener) {
unhook(Events.Activate, listener);
unhook(Events.Deactivate, listener);
unhook(Events.Close, listener);
}
/**
* Sets the shell's header icon.
*
* @param style the icon style
*/
public void setIconStyle(String style) {
header.setIconStyle(style);
}
/**
* Sets the shell's header text.
*
* @param text the text
*/
public void setText(String text) {
header.setText(text);
}
protected void activateShell(Event event) {
// ignore close button
if (closeBtn != null) {
if (DOM.isOrHasChild(closeBtn.getElement(), DOM.eventGetTarget(event))) {
return;
}
}
ShellManager.get().setActive(this);
}
protected void afterHide() {
RootPanel.get().remove(this);
if (MyGWT.isIE && !MyGWT.isIE7) {
framePanel.onHide(getElement());
}
if (modal != null) {
modal.hide();
}
if (autoHidePreview != null) {
DOM.removeEventPreview(autoHidePreview);
}
showing = false;
fireEvent(Events.Close);
}
protected void afterShow() {
if (autoHidePreview != null) {
DOM.addEventPreview(autoHidePreview);
}
fireEvent(Events.Show);
}
protected void createContent(WidgetContainer container) {
}
protected void doAttachChildren() {
WidgetHelper.doAttach(header);
WidgetHelper.doAttach(content);
}
protected void doDetachChildren() {
WidgetHelper.doDetach(header);
WidgetHelper.doDetach(content);
}
protected void onRender() {
super.onRender();
setElement(DOM.createDiv());
setStyleName("my-shell");
MyDOM.setStyleAttribute(getElement(), "position", "absolute");
DOM.appendChild(getElement(), header.getElement());
String html = Format.substitue(Markup.BOTTOM_BOX, "my-shell-body");
bodyElem = MyDOM.create(html);
contentElem = MyDOM.findChild("my-shell-body-mc", bodyElem);
footerElem = MyDOM.findChild("my-shell-body-bc", bodyElem);
DOM.appendChild(getElement(), bodyElem);
DOM.appendChild(contentElem, content.getElement());
// close button
if ((style & Style.CLOSE) != 0) {
closeBtn = new IconButton("my-tool-close");
closeBtn.addStyleName("my-tool");
closeBtn.addListener(Events.Click, new Listener() {
public void handleEvent(BaseEvent be) {
close();
}
});
header.addButton(closeBtn);
closeBtn.removeStyleName("my-tool");
}
if ((style & Style.AUTO_HIDE) != 0) {
autoHidePreview = new EventPreview() {
public boolean onEventPreview(Event event) {
Element target = DOM.eventGetTarget(event);
if (DOM.isOrHasChild(getElement(), target)) {
return true;
}
if (DOM.eventGetType(event) == Event.ONCLICK) {
close();
}
return false;
}
};
}
// resizable
if ((style & Style.RESIZE) != 0) {
resizer = new Resizable(this);
resizer.minWidth = 200;
resizer.minHeight = 100;
}
if ((style & Style.MODAL) != 0) {
modal = new ModalPanel();
}
dragListener = new Listener() {
public void handleEvent(BaseEvent be) {
switch (be.type) {
case Events.DragStart:
if (shadow != null) {
shadow.setVisible(false);
}
break;
case Events.DragEnd:
if (shadow != null) {
shadow.setVisible(true);
shadow.sync(getBounds());
}
break;
}
}
};
draggable = new Draggable(this, header);
draggable.useProxy = false;
draggable.addListener(Events.DragStart, dragListener);
draggable.addListener(Events.DragEnd, dragListener);
shadow = new Shadow(shadowPosition);
sinkEvents(Event.MOUSEEVENTS);
}
protected void onResize(int width, int height) {
int h = height;
int w = width;
w -= 12;
w -= MyDOM.getDecorationWidth(contentElem, Style.LEFT | Style.RIGHT);
h -= header.getHeight();
if (!MyDOM.isVisibleBox()) {
h -= 2;
}
h -= MyDOM.getHeight(footerElem);
MyDOM.setSize(contentElem, w, h, true);
MyDOM.setSize(content.getElement(), w, h, true);
content.layout();
}
}