/*
* InlineToolbarButton.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.core.client.widget;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import org.rstudio.core.client.StringUtil;
public class InlineToolbarButton extends Composite implements HasButtonMethods
{
private class SimpleHasHandlers extends HandlerManager implements HasHandlers
{
private SimpleHasHandlers()
{
super(null);
}
}
interface Binder extends UiBinder<Widget, InlineToolbarButton>
{}
@UiConstructor
public InlineToolbarButton(ImageResource icon,
String label,
String description)
{
icon_ = new Image(icon);
label_ = new SpanLabel(label, false);
zeroHeightPanel_ = new ZeroHeightPanel(icon.getWidth(),
icon.getHeight(),
4);
if (StringUtil.isNullOrEmpty(label))
label_.setVisible(false);
initWidget(GWT.<Binder>create(Binder.class).createAndBindUi(this));
setTitle(description);
}
public void setLabel(String text)
{
label_.setText(text);
label_.setVisible(!StringUtil.isNullOrEmpty(text));
}
public void setMenu(final ToolbarPopupMenu menu)
{
/*
* We want clicks on this button to toggle the visibility of the menu,
* as well as having the menu auto-hide itself as it normally does.
* It's necessary to manually track the visibility (menuShowing) because
* in the case where the menu is showing, clicking on this button first
* causes the menu to auto-hide and then our mouseDown handler is called
* (so we can't rely on menu.isShowing(), it'll always be false by the
* time you get into the mousedown handler).
*/
final boolean[] menuShowing = new boolean[1];
addDomHandler(new MouseDownHandler()
{
public void onMouseDown(MouseDownEvent event)
{
event.preventDefault();
event.stopPropagation();
if (menuShowing[0])
{
menu.hide();
}
else
{
menu.setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight)
{
menu.setPopupPosition(
InlineToolbarButton.this.getAbsoluteLeft(),
InlineToolbarButton.this.getAbsoluteTop() +
InlineToolbarButton.this.getOffsetHeight() +
8 /* toolbar area under botton */);
}
});
}
menuShowing[0] = true;
}
}, MouseDownEvent.getType());
menu.addCloseHandler(new CloseHandler<PopupPanel>()
{
public void onClose(CloseEvent<PopupPanel> popupPanelCloseEvent)
{
Scheduler.get().scheduleDeferred(new ScheduledCommand()
{
public void execute()
{
menuShowing[0] = false;
}
});
}
});
}
@Override
public HandlerRegistration addClickHandler(ClickHandler clickHandler)
{
/*
* When we directly subscribe to this widget's ClickEvent, sometimes the
* click gets ignored (inconsistent repro but it happens enough to be
* annoying). Doing it this way fixes it.
*/
hasHandlers_.addHandler(ClickEvent.getType(), clickHandler);
final HandlerRegistration mouseDown = addDomHandler(new MouseDownHandler()
{
public void onMouseDown(MouseDownEvent event)
{
event.preventDefault();
event.stopPropagation();
down_ = true;
}
}, MouseDownEvent.getType());
final HandlerRegistration mouseOut = addDomHandler(new MouseOutHandler()
{
public void onMouseOut(MouseOutEvent event)
{
event.preventDefault();
event.stopPropagation();
down_ = false;
}
}, MouseOutEvent.getType());
final HandlerRegistration mouseUp = addDomHandler(new MouseUpHandler()
{
public void onMouseUp(MouseUpEvent event)
{
event.preventDefault();
event.stopPropagation();
if (down_)
{
down_ = false;
NativeEvent clickEvent = Document.get().createClickEvent(
1,
event.getScreenX(),
event.getScreenY(),
event.getClientX(),
event.getClientY(),
event.getNativeEvent().getCtrlKey(),
event.getNativeEvent().getAltKey(),
event.getNativeEvent().getShiftKey(),
event.getNativeEvent().getMetaKey());
DomEvent.fireNativeEvent(clickEvent, hasHandlers_);
}
}
}, MouseUpEvent.getType());
return new HandlerRegistration()
{
public void removeHandler()
{
mouseDown.removeHandler();
mouseOut.removeHandler();
mouseUp.removeHandler();
}
};
}
public void click()
{
NativeEvent clickEvent = Document.get().createClickEvent(
1,
0,
0,
0,
0,
false,
false,
false,
false);
DomEvent.fireNativeEvent(clickEvent, hasHandlers_);
}
@UiField(provided = true)
Image icon_;
@UiField(provided = true)
SpanLabel label_;
@UiField(provided = true)
ZeroHeightPanel zeroHeightPanel_;
private SimpleHasHandlers hasHandlers_ = new SimpleHasHandlers();
private boolean down_;
}