/*
* Copyright 2012 GWT-Bootstrap
*
* 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 com.github.gwtbootstrap.client.ui;
import java.util.HashSet;
import java.util.Set;
import com.github.gwtbootstrap.client.ui.base.DivWidget;
import com.github.gwtbootstrap.client.ui.base.HasVisibility;
import com.github.gwtbootstrap.client.ui.base.HasVisibleHandlers;
import com.github.gwtbootstrap.client.ui.base.IsAnimated;
import com.github.gwtbootstrap.client.ui.constants.BackdropType;
import com.github.gwtbootstrap.client.ui.constants.Constants;
import com.github.gwtbootstrap.client.ui.constants.DismissType;
import com.github.gwtbootstrap.client.ui.event.HiddenEvent;
import com.github.gwtbootstrap.client.ui.event.HiddenHandler;
import com.github.gwtbootstrap.client.ui.event.HideEvent;
import com.github.gwtbootstrap.client.ui.event.HideHandler;
import com.github.gwtbootstrap.client.ui.event.ShowEvent;
import com.github.gwtbootstrap.client.ui.event.ShowHandler;
import com.github.gwtbootstrap.client.ui.event.ShownEvent;
import com.github.gwtbootstrap.client.ui.event.ShownHandler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
//@formatter:off
/**
* Popup dialog with optional header and {@link ModalFooter footer.}
* <p>
* By default, all other Modals are closed once a new one is opened. This
* setting can be {@link #setHideOthers(boolean) overridden.}
*
* <p>
* <h3>UiBinder Usage:</h3>
*
* <pre>
* {@code
* <b:Modal title="My Modal" backdrop="STATIC">
* <g:Label>Modal Content!</g:Label>
* <b:ModalFooter>
* <b:Button icon="FILE">Save</b:Button>
* </b:ModalFooter>
* </b:Modal>
* }
* </pre>
*
* All arguments are optional.
* </p>
*
* @since 2.0.4.0
*
* @author Carlos Alexandro Becker
*
* @author Dominik Mayer
*
* @see <a
* href="http://twitter.github.com/bootstrap/javascript.html#modals">Bootstrap
* documentation</a>
* @see PopupPanel
*/
// @formatter:on
public class Modal extends DivWidget implements HasVisibility, HasVisibleHandlers, IsAnimated {
private static Set<Modal> currentlyShown = new HashSet<Modal>();
private final DivWidget header = new DivWidget();
private final DivWidget body = new DivWidget("modal-body");
private boolean keyboard = true;
private BackdropType backdropType = BackdropType.NORMAL;
private boolean show = false;
private boolean hideOthers = true;
private boolean configured = false;
private Close close = new Close(DismissType.MODAL);
private String title;
/**
* Creates an empty, hidden widget.
*/
public Modal() {
super("modal");
super.add(header);
super.add(body);
setVisible(false);
}
/**
* Creates an empty, hidden widget with specified show behavior.
*
* @param animated
* <code>true</code> if the widget should be animated.
*
*/
public Modal(boolean animated) {
this(animated, false);
}
/**
* Creates an empty, hidden widget with specified show behavior.
*
* @param animated
* <code>true</code> if the widget should be animated.
*
* @param dynamicSafe
* <code>true</code> removes from RootPanel when hidden
*/
public Modal(boolean animated,
boolean dynamicSafe) {
this();
setAnimation(animated);
setDynamicSafe(dynamicSafe);
}
/**
* Setup the modal to prevent memory leaks. When modal is hidden, will
* remove all event handlers, and them remove the modal DOM from document
* DOM.
*
* Default is false.
*
* @param dynamicSafe
*/
public void setDynamicSafe(boolean dynamicSafe) {
if (dynamicSafe) {
addHiddenHandler(new HiddenHandler() {
@Override
public void onHidden(HiddenEvent hiddenEvent) {
unsetHandlerFunctions(getElement());
Modal.this.removeFromParent();
}
});
}
}
/**
* Sets the title of the Modal.
*
* @param title
* the title of the Modal
*/
@Override
public void setTitle(String title) {
this.title = title;
header.clear();
if (title == null || title.isEmpty()) {
showHeader(false);
} else {
header.add(close);
header.add(new Heading(3, title));
showHeader(true);
}
}
private void showHeader(boolean show) {
if (show)
header.setStyleName(Constants.MODAL_HEADER);
else
header.removeStyleName(Constants.MODAL_HEADER);
}
/**
* {@inheritDoc}
*/
public void setAnimation(boolean animated) {
if (animated)
addStyleName(Constants.FADE);
else
removeStyleName(Constants.FADE);
}
/**
* {@inheritDoc}
*/
public boolean getAnimation() {
return getStyleName().contains(Constants.FADE);
}
/**
* Sets whether this Modal appears on top of others or is the only one
* visible on screen.
*
* @param hideOthers
* <code>true</code> to make sure that this modal is the only one
* shown. All others will be hidden. Default: <code>true</code>
*/
public void setHideOthers(boolean hideOthers) {
this.hideOthers = hideOthers;
}
/**
* Sets whether the Modal is closed when the <code>ESC</code> is pressed.
*
* @param keyboard
* <code>true</code> if the Modal is closed by <code>ESC</code>
* key. Default: <code>true</code>
*/
public void setKeyboard(boolean keyboard) {
this.keyboard = keyboard;
reconfigure();
}
/**
* Get Keyboard enable state
*
* @return true:enable false:disable
*/
public boolean isKeyboardEnable() {
return this.keyboard;
}
/**
* Sets the type of the backdrop.
*
* @param type
* the backdrop type
*/
public void setBackdrop(BackdropType type) {
backdropType = type;
reconfigure();
}
/**
* Get backdrop type.
*
* @return backdrop type.
*/
public BackdropType getBackdropType() {
return this.backdropType;
}
/**
* Reconfigures the modal with changed settings.
*/
protected void reconfigure() {
if (configured) {
reconfigure(keyboard, backdropType, show);
}
}
/**
* {@inheritDoc}
*/
@Override
public void add(Widget w) {
if (w instanceof ModalFooter) {
super.add(w);
} else
body.add(w);
}
/**
* {@inheritDoc}
*/
@Override
public void insert(Widget w, int beforeIndex) {
body.insert(w, beforeIndex);
}
/**
* {@inheritDoc}
*/
public void show() {
if (!this.isAttached()) {
RootPanel.get().add(this);
}
changeVisibility("show");
}
@Override
protected void onAttach() {
super.onAttach();
configure(keyboard, backdropType, show);
setHandlerFunctions(getElement());
configured = true;
}
/**
* {@inheritDoc}
*/
public void hide() {
changeVisibility("hide");
}
/**
* {@inheritDoc}
*/
public void toggle() {
changeVisibility("toggle");
}
private void changeVisibility(String visibility) {
changeVisibility(getElement(), visibility);
}
/**
* This method is called immediately when the widget's {@link #hide()}
* method is executed.
*/
protected void onHide() {
fireEvent(new HideEvent());
}
/**
* This method is called once the widget is completely hidden.
*/
protected void onHidden() {
fireEvent(new HiddenEvent());
currentlyShown.remove(this);
}
/**
* This method is called immediately when the widget's {@link #show()}
* method is executed.
*/
protected void onShow() {
if (hideOthers)
hideShownModals();
fireEvent(new ShowEvent());
}
private void hideShownModals() {
for (Modal m : currentlyShown)
m.hide();
}
/**
* This method is called once the widget is completely shown.
*/
protected void onShown() {
fireEvent(new ShownEvent());
currentlyShown.add(this);
}
private void reconfigure(boolean keyboard, BackdropType backdropType, boolean show) {
if (backdropType == BackdropType.NORMAL) {
reconfigure(getElement(), keyboard, true, show);
} else if (backdropType == BackdropType.NONE) {
reconfigure(getElement(), keyboard, false, show);
} else if (backdropType == BackdropType.STATIC) {
reconfigure(getElement(), keyboard, BackdropType.STATIC.get(), show);
}
}
private void configure(boolean keyboard, BackdropType backdropType, boolean show) {
if (backdropType == BackdropType.NORMAL) {
configure(getElement(), keyboard, true, show);
} else if (backdropType == BackdropType.NONE) {
configure(getElement(), keyboard, false, show);
} else if (backdropType == BackdropType.STATIC) {
configure(getElement(), keyboard, BackdropType.STATIC.get(), show);
}
}
//@formatter:off
private native void reconfigure(Element e, boolean k, boolean b, boolean s) /*-{
var modal = null;
if($wnd.jQuery(e).data('modal')) {
modal = $wnd.jQuery(e).data('modal');
$wnd.jQuery(e).removeData('modal');
}
$wnd.jQuery(e).modal({
keyboard : k,
backdrop : b,
show : s
});
if(modal) {
$wnd.jQuery(e).data('modal').isShown = modal.isShown;
}
}-*/;
private native void reconfigure(Element e, boolean k, String b, boolean s) /*-{
var modal = null;
if($wnd.jQuery(e).data('modal')) {
modal = $wnd.jQuery(e).data('modal');
$wnd.jQuery(e).removeData('modal');
}
$wnd.jQuery(e).modal({
keyboard : k,
backdrop : b,
show : s
});
if(modal) {
$wnd.jQuery(e).data('modal').isShown = modal.isShown;
}
}-*/;
private native void configure(Element e, boolean k, boolean b, boolean s) /*-{
$wnd.jQuery(e).modal({
keyboard : k,
backdrop : b,
show : s
});
}-*/;
private native void configure(Element e, boolean k, String b, boolean s) /*-{
$wnd.jQuery(e).modal({
keyboard : k,
backdrop : b,
show : s
});
}-*/;
private native void changeVisibility(Element e, String visibility) /*-{
$wnd.jQuery(e).modal(visibility);
}-*/;
/**
* Links the Java functions that fire the events.
*/
private native void setHandlerFunctions(Element e) /*-{
var that = this;
$wnd.jQuery(e).on('hide', function() {
that.@com.github.gwtbootstrap.client.ui.Modal::onHide()();
});
$wnd.jQuery(e).on('hidden', function() {
that.@com.github.gwtbootstrap.client.ui.Modal::onHidden()();
});
$wnd.jQuery(e).on('show', function() {
that.@com.github.gwtbootstrap.client.ui.Modal::onShow()();
});
$wnd.jQuery(e).on('shown', function() {
that.@com.github.gwtbootstrap.client.ui.Modal::onShown()();
});
}-*/;
/**
* Unlinks all the Java functions that fire the events.
*/
private native void unsetHandlerFunctions(Element e) /*-{
$wnd.jQuery(e).off('hide');
$wnd.jQuery(e).off('hidden');
$wnd.jQuery(e).off('show');
$wnd.jQuery(e).off('shown');
}-*/;
//@formatter:on
/**
* {@inheritDoc}
*/
public HandlerRegistration addHideHandler(HideHandler handler) {
return addHandler(handler, HideEvent.getType());
}
/**
* {@inheritDoc}
*/
public HandlerRegistration addHiddenHandler(HiddenHandler handler) {
return addHandler(handler, HiddenEvent.getType());
}
/**
* {@inheritDoc}
*/
public HandlerRegistration addShowHandler(ShowHandler handler) {
return addHandler(handler, ShowEvent.getType());
}
/**
* {@inheritDoc}
*/
public HandlerRegistration addShownHandler(ShownHandler handler) {
return addHandler(handler, ShownEvent.getType());
}
/**
* Show/Hide close button. The Modal must have a title.
*
* @param visible
* <b>true</b> for show and <b>false</b> to hide. Defaults is
* <b>true</b>.
*/
public void setCloseVisible(boolean visible) {
close.getElement().getStyle().setVisibility(visible
? Style.Visibility.VISIBLE
: Style.Visibility.HIDDEN);
}
/**
* @deprecated modal do not support setSize method
*/
@Override
public void setSize(String width, String height) {
throw new UnsupportedOperationException("modal do not support setSize method");
}
}