/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.gwt.user.client.ui;
import org.xwiki.gwt.user.client.DragAdaptor;
import org.xwiki.gwt.user.client.DragListener;
import org.xwiki.gwt.user.client.Images;
import org.xwiki.gwt.user.client.Strings;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* Generic dialog box with optimized dragging.
*
* @version $Id: c5efc7d81f80ffabb41e70b8053c59be84faabed $
*/
public class DialogBox extends PopupPanel implements DragListener, ClickHandler
{
/**
* The style name used when the dialog is dragged.
*/
public static final String DRAGGING_STYLE = "dragging";
/**
* The image on the left of the caption bar.
*/
private Image icon;
/**
* The text on the caption bar.
*/
private final Label caption;
/**
* The close icon on the right of the caption bar.
*/
private final Image closeIcon;
/**
* The bar at the top of the dialog holding the icon, the title and close button.
*/
private final FlowPanel captionBar;
/**
* The content of the dialog.
*/
private FlowPanel content;
/**
* Temporary panel that replaces dialog's content while the dialog box is moved. It is used for optimized dragging.
*/
private final FlowPanel contentPlaceHolder;
/**
* The dialog box containing the caption bar, the content and the content place holder.
*/
private final FlowPanel box;
/**
* The horizontal coordinate of the point where the drag started.
*/
private int dragStartX;
/**
* The vertical coordinate of the point where the drag started.
*/
private int dragStartY;
/**
* Creates a new dialog that doesn't auto hide and is not modal.
*/
public DialogBox()
{
this(false);
}
/**
* Creates a new dialog that is not modal.
*
* @param autoHide Whether or not the dialog should auto hide when the user clicks outside of it.
*/
public DialogBox(boolean autoHide)
{
this(autoHide, false);
}
/**
* Creates a new dialog.
*
* @param autoHide Whether or not the dialog should auto hide when the user clicks outside of it.
* @param modal Specifies if the dialog box can loose focus.
*/
public DialogBox(boolean autoHide, boolean modal)
{
// We use only the glass panel to make the dialog box modal because otherwise the RichTextArea would not be
// usable on a modal dialog box. If we make the popup panel modal it will stop all the events whose target is
// not a child of the panel. Events triggered inside the rich text area are from a different document than the
// one holding the panel and thus their target is not a child of the panel.
super(autoHide, false);
setGlassEnabled(modal);
caption = new Label();
caption.addStyleName("xDialogCaption");
(new DragAdaptor(caption)).addDragListener(this);
closeIcon = new Image(Images.INSTANCE.close());
closeIcon.setTitle(Strings.INSTANCE.close());
closeIcon.addStyleName("xDialogCloseIcon");
closeIcon.addClickHandler(this);
captionBar = new FlowPanel();
captionBar.addStyleName("xDialogCaptionBar");
captionBar.add(caption);
captionBar.add(closeIcon);
content = new FlowPanel();
content.addStyleName("xDialogContent");
contentPlaceHolder = new FlowPanel();
contentPlaceHolder.setVisible(false);
box = new FlowPanel();
box.setStylePrimaryName("xDialogBox");
box.add(captionBar);
box.add(content);
box.add(contentPlaceHolder);
super.setWidget(box);
}
/**
* @return the text of the caption bar
*/
public String getCaption()
{
return caption.getText();
}
/**
* Sets the text of the caption bar.
*
* @param caption the string to be placed on the caption bar
*/
public void setCaption(String caption)
{
this.caption.setText(caption);
}
/**
* @return the image on the left of the caption bar
*/
public Image getIcon()
{
return icon;
}
/**
* Sets the icon on left of the caption bar.
*
* @param icon the image to placed on the left of the caption bar
*/
public void setIcon(Image icon)
{
if (this.icon != null) {
this.icon.removeFromParent();
}
this.icon = icon;
icon.addStyleName("xDialogIcon");
captionBar.insert(icon, 0);
}
@Override
public Widget getWidget()
{
if (content.getWidgetCount() > 0) {
return content.getWidget(0);
} else {
return null;
}
}
@Override
public void setWidget(Widget widget)
{
content.clear();
content.add(widget);
}
@Override
protected void onPreviewNativeEvent(NativePreviewEvent event)
{
// 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) && isCaptionEvent(nativeEvent)) {
nativeEvent.preventDefault();
}
super.onPreviewNativeEvent(event);
}
/**
* @param event a native event
* @return {@code true} if the target of the given event is the caption bar or one of its descendants, {@code false}
* otherwise
*/
private boolean isCaptionEvent(NativeEvent event)
{
EventTarget target = event.getEventTarget();
if (Element.is(target)) {
return captionBar.getElement().isOrHasChild(Element.as(target));
}
return false;
}
@Override
public void onDragStart(Widget sender, int x, int y)
{
dragStartX = x;
dragStartY = y;
contentPlaceHolder.setPixelSize(content.getOffsetWidth(), content.getOffsetHeight());
content.setVisible(false);
contentPlaceHolder.setVisible(true);
box.addStyleDependentName(DRAGGING_STYLE);
}
@Override
public void onDrag(Widget sender, int x, int y)
{
if (sender == caption) {
int absX = x + getAbsoluteLeft();
int absY = y + getAbsoluteTop();
setPopupPosition(absX - dragStartX, absY - dragStartY);
}
}
@Override
public void onDragEnd(Widget sender, int x, int y)
{
contentPlaceHolder.setVisible(false);
content.setVisible(true);
box.removeStyleDependentName(DRAGGING_STYLE);
}
@Override
public void onClick(ClickEvent event)
{
if (event.getSource() == closeIcon) {
hide();
}
}
}