/*
* LocatorPanel.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.studio.client.workbench.views.plots;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.*;
import org.rstudio.core.client.Point;
import org.rstudio.core.client.layout.FadeOutAnimation;
import org.rstudio.core.client.theme.res.ThemeStyles;
import org.rstudio.core.client.widget.HasCustomizableToolbar.Customizer;
import org.rstudio.core.client.widget.SmallButton;
import org.rstudio.core.client.widget.Toolbar;
import org.rstudio.studio.client.common.icons.StandardIcons;
import java.util.ArrayList;
public class LocatorPanel extends LayoutPanel
implements Locator.Display, HasSelectionHandlers<Point>
{
public LocatorPanel()
{
setStylePrimaryName(ThemeStyles.INSTANCE.locatorPanel());
feedbackImage_ = new Image(StandardIcons.INSTANCE.click_feedback());
feedbackImage_.setVisible(false);
add(feedbackImage_);
setWidgetTopHeight(feedbackImage_, 0, Unit.PX, FB_HEIGHT, Unit.PX);
setWidgetLeftWidth(feedbackImage_, 0, Unit.PX, FB_WIDTH, Unit.PX);
inputPanel_ = new InputPanel();
inputPanel_.setSize("100%", "100%");
add(inputPanel_);
setWidgetLeftRight(inputPanel_, 0, Unit.PX, 0, Unit.PX);
setWidgetTopBottom(inputPanel_, 0, Unit.PX, 0, Unit.PX);
inputPanel_.addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
Element el = inputPanel_.getElement();
int x = event.getNativeEvent().getClientX();
int y = event.getNativeEvent().getClientY();
Point p = new Point(
x - el.getAbsoluteLeft(), y - el.getAbsoluteTop());
showFeedbackAt(p);
SelectionEvent.fire(LocatorPanel.this, p);
}
});
// fill entire area of parent container
setSize("100%", "100%");
}
public void showLocator(Plots.Parent parent)
{
// set parent
parent_ = parent ;
// add to parent
parent_.add(this);
// add custom locator toolbar to parent
installCustomToolbar();
// subscribe to ESC key browser-wide for dismissal of the Locator UI
// (unhook any existing subscription first)
unhookNativePreviewHandler();
escHandlerReg_ = Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event)
{
if (event.getTypeInt() == Event.ONKEYDOWN
&& event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE)
{
event.cancel();
SelectionEvent.fire(LocatorPanel.this, null);
}
}
});
}
public void hideLocator()
{
// make sure we unsubscribe from events
unhookNativePreviewHandler();
// ice custom toolbar
parent_.removeCustomToolbar();
// remove from parent
removeFromParent();
}
public HandlerRegistration addSelectionHandler(
SelectionHandler<Point> handler)
{
return addHandler(handler, SelectionEvent.getType());
}
@Override
protected void onUnload()
{
// make sure we unsubscribe from events
unhookNativePreviewHandler();
// nix any existing feedback
cancelFeedback();
// super
super.onUnload();
}
private void installCustomToolbar()
{
parent_.installCustomToolbar(new Customizer() {
public void setToolbarContents(Toolbar toolbar)
{
toolbar.addLeftWidget(
new Label("Locator active (Esc to finish)"));
SmallButton doneButton = new SmallButton("Finish");
doneButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event)
{
SelectionEvent.fire(LocatorPanel.this, null);
}
});
toolbar.addRightWidget(doneButton);
toolbar.addRightWidget(new HTML(" "));
}
});
}
private void showFeedbackAt(Point p)
{
cancelFeedback();
setWidgetTopHeight(feedbackImage_, p.getY() - FB_OFFSET_Y,
Unit.PX, FB_HEIGHT, Unit.PX);
setWidgetLeftWidth(feedbackImage_, p.getX() - FB_OFFSET_X,
Unit.PX, FB_WIDTH, Unit.PX);
forceLayout();
feedbackImage_.setVisible(true);
feedbackImage_.getElement().getStyle().setOpacity(1.0);
feedbackTimer_ = new Timer()
{
@Override
public void run()
{
feedbackTimer_ = null;
ArrayList<Widget> widgets = new ArrayList<Widget>();
widgets.add(feedbackImage_);
feedbackAnimation_ = new FadeOutAnimation(widgets,
new Command() {
public void execute()
{
feedbackAnimation_ = null;
}
});
feedbackAnimation_.run(300);
}
};
feedbackTimer_.schedule(700);
}
private void cancelFeedback()
{
if (feedbackTimer_ != null)
{
feedbackTimer_.cancel();
feedbackTimer_ = null;
}
if (feedbackAnimation_ != null)
{
feedbackAnimation_.cancel();
feedbackAnimation_ = null;
}
}
private void unhookNativePreviewHandler()
{
if (escHandlerReg_ != null)
{
escHandlerReg_.removeHandler();
escHandlerReg_ = null;
}
}
private static class InputPanel extends SimplePanel
{
public HandlerRegistration addClickHandler(ClickHandler handler)
{
return addDomHandler(handler, ClickEvent.getType());
}
}
private Plots.Parent parent_;
private Timer feedbackTimer_;
private Animation feedbackAnimation_;
private Image feedbackImage_;
private static final int FB_HEIGHT = 24;
private static final int FB_WIDTH = 24;
private static final int FB_OFFSET_Y = 23;
private static final int FB_OFFSET_X = 0;
private InputPanel inputPanel_;
private HandlerRegistration escHandlerReg_ = null;
}