package com.griddynamics.jagger.webclient.client.components;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.ResizeComposite;
import com.google.gwt.user.client.ui.NativeHorizontalScrollbar;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.googlecode.gflot.client.Series;
import com.googlecode.gflot.client.SeriesData;
import com.googlecode.gflot.client.SimplePlot;
import com.googlecode.gflot.client.Zoom;
import com.griddynamics.jagger.dbapi.model.MetricNode;
import com.sencha.gxt.widget.core.client.tree.Tree;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Class that hold widgets of type PlotContainer with dynamic layout feature.
*/
public class PlotsPanel extends ResizeComposite {
interface PlotsPanelUiBinder extends UiBinder<Widget, PlotsPanel> {
}
private static PlotsPanelUiBinder ourUiBinder = GWT.create(PlotsPanelUiBinder.class);
@UiField
/* Main layout panel where all children will be */
protected DynamicLayoutPanel<PlotContainer> layoutPanel;
@UiField
/* Menu bar to control plot panel */
protected PlotButtonsPanel plotButtonsPanel;
@UiField
/* Scroll bar for layout panel */
protected ScrollPanel scrollPanelMetrics;
private ControlTree<String> controlTree;
public PlotsPanel() {
initWidget(ourUiBinder.createAndBindUi(this));
plotButtonsPanel.setupButtonPanel(this);
layoutPanel.setChildHeight(plotButtonsPanel.getPlotHeight());
}
public void setControlTree(ControlTree<String> controlTree) {
this.controlTree = controlTree;
}
/**
* Deselect metric node in control tree. This will lead to plot removal from plot panel
* @param metricNode metric node */
public void deselectMetricNode(MetricNode metricNode) {
controlTree.setChecked(metricNode, Tree.CheckState.UNCHECKED);
}
/**
* Remove widgets from layout panel by metric node
* @param metricNodes metric nodes */
public void removeByMetricNodes(Collection<MetricNode> metricNodes) {
Set<String> ids = new HashSet<String>();
for (MetricNode metricNode : metricNodes) {
ids.add(metricNode.getId());
}
layoutPanel.removeChildren(ids);
childrenCount = layoutPanel.getAllChildren().size();
setMaxRange();
}
/**
* Remove all widgets from layoutPanel */
public void clear() {
layoutPanel.clear();
childrenCount = 0;
}
/**
* Add widget to layoutPanel
* @param plotContainer child widget */
public void addElement(final PlotContainer plotContainer) {
plotContainer.setHeight(plotButtonsPanel.getPlotHeight() + "px");
plotContainer.setPlotsPanel(this);
scrollCalculations(plotContainer);
plotContainer.getPlotRepresentation().addAttachHandler(new AttachEvent.Handler() {
@Override
public void onAttachOrDetach(AttachEvent event) {
// executes when plot have been loaded
plotContainer.getPlotRepresentation().calculateScrollWidth();
avalancheScrollEventsCount ++;
plotContainer.getPlotRepresentation().panToPercent(percent);
}
});
layoutPanel.setAdditionalHeightForChild(
plotContainer.getDragPanelHeight()
+ plotContainer.getPlotRepresentation().getZoomPanelHeight()
+ plotContainer.getPlotRepresentation().getScrollPanelHeight()
+ plotContainer.getPlotRepresentation().getXAxisLabelHeight()
);
layoutPanel.addChild(plotContainer);
childrenCount = layoutPanel.getAllChildren().size();
setMaxRange();
}
private void panAllPlots(double percent) {
for (PlotContainer pc : layoutPanel.getAllChildren()) {
pc.getPlotRepresentation().panToPercent(percent);
}
}
private void scrollCalculations(final PlotContainer plotContainer) {
double maxX = calculateMaxXAxisValue(plotContainer.getPlotRepresentation().getSimplePlot());
double maxRange;
if (this.isEmpty()) {
maxRange = maxX;
} else {
if (maxX > getMaxXAxisValue()) {
maxRange = maxX;
} else {
maxRange = getMaxXAxisValue();
}
}
plotContainer.getPlotRepresentation().setMaxRange(maxRange);
final NativeHorizontalScrollbar scrollBar = plotContainer.getPlotRepresentation().getScrollbar();
scrollBar.setVisible(true);
scrollBar.addScrollHandler(new ScrollHandler() {
@Override
public void onScroll(ScrollEvent event) {
if (avalancheScrollEventsCount > 0) {
avalancheScrollEventsCount --;
return;
}
avalancheScrollEventsCount = childrenCount;
int currentPosition = scrollBar.getHorizontalScrollPosition();
double percent = 1D * (currentPosition - scrollBar.getMinimumHorizontalScrollPosition()) /
(scrollBar.getMaximumHorizontalScrollPosition() - scrollBar.getMinimumHorizontalScrollPosition());
PlotsPanel.this.percent = percent;
panAllPlots(percent);
}
});
}
/**
* Global counter to see how many avalanche scroll events hav not been finished */
private int avalancheScrollEventsCount = 0;
/**
* To avoid calculating on every scroll event */
private int childrenCount = 0;
/**
* Current state of plot`s scrolls */
private double percent = 0;
private void setMaxRange() {
for (PlotContainer pc : layoutPanel.getAllChildren()) {
pc.getPlotRepresentation().setMaxRange(getMaxXAxisValue());
}
}
/**
* Check if PlotsPanel contains element with certain id
* @param plotId id of element to identify
* @return true if element found with given plotId, false otherwise */
public boolean containsElementWithId(String plotId) {
return layoutPanel.containsElementWithId(plotId);
}
/**
* Zoom all plots in PlotsPanel */
public void zoomIn() {
zoom(false);
}
/**
* Zoom out all plots in PlotsPanel */
public void zoomOut() {
zoom(true);
}
/**
* Zoom in if param is false, zoom out otherwise.
* @param out defines whether zoom in or out.
*/
private void zoom(boolean out) {
double maxRange = layoutPanel.getFirstChild().getPlotRepresentation().getMaxRange();
for (PlotContainer pc : layoutPanel.getAllChildren()) {
SimplePlot plot = pc.getPlotRepresentation().getSimplePlot();
Zoom zoom = Zoom.create().setAmount(1.1);
if (out) {
plot.zoomOut(zoom);
} else {
plot.zoom(zoom);
}
pc.getPlotRepresentation().calculateScrollWidth();
}
PlotRepresentation plotRepresentation = layoutPanel.getFirstChild().getPlotRepresentation();
double percent;
double minVisible = plotRepresentation.getSimplePlot().getAxes().getX().getMinimumValue();
double maxVisible = plotRepresentation.getSimplePlot().getAxes().getX().getMaximumValue();
if (out) {
if (maxVisible >= maxRange && minVisible <= 0) {
// do nothing when plot in visible range
return;
}
if (maxVisible >= maxRange) {
// to the end
plotRepresentation.panToPercent(1);
return;
} else if (minVisible <= 0) {
// to very start
plotRepresentation.panToPercent(0);
return;
}
}
percent = minVisible / (maxRange - maxVisible + minVisible);
plotRepresentation.panToPercent(percent);
}
/**
* Zoom to size of given plot;
* @param plot - given plot */
public void zoomDefault(SimplePlot plot) {
double xMaxValue = calculateMaxXAxisValue(plot);
for (PlotContainer pc : layoutPanel.getAllChildren()) {
SimplePlot currentPlot = pc.getPlotRepresentation().getSimplePlot();
// currently we always start xAxis with zero
currentPlot.getOptions().getXAxisOptions().setMinimum(0).setMaximum(xMaxValue);
currentPlot.setupGrid();
currentPlot.redraw();
pc.getPlotRepresentation().calculateScrollWidth();
}
// all plots start with zero
PlotRepresentation plotRepresentation = layoutPanel.getFirstChild().getPlotRepresentation();
plotRepresentation.panToPercent(0);
}
/**
* Returns max X axis value on plot
* @param plot plot
* @return max X axis value */
private double calculateMaxXAxisValue (SimplePlot plot) {
JsArray<Series> seriesArray = plot.getModel().getSeries();
double maxValue = Double.MIN_VALUE;
for (int i = 0; i < seriesArray.length(); i ++) {
// get curve
SeriesData curve = seriesArray.get(i).getData();
double temp = curve.getX(curve.length() - 1);
if (maxValue < temp) {
maxValue = temp;
}
}
return maxValue;
}
/**
* Check if PlotsPanel contains any plots.
* @return true if it is empty, false otherwise */
public boolean isEmpty() {
return childrenCount == 0;
}
/**
* @return maximum X axis value */
public double getMaxXAxisValue() {
// no widgets in panel
assert layoutPanel.getWidgetCount() > 0;
double xMaxValue = Double.MIN_VALUE;
for (PlotContainer pc : layoutPanel.getAllChildren()) {
double curveMaxX = calculateMaxXAxisValue(pc.getPlotRepresentation().getSimplePlot());
if (curveMaxX > xMaxValue) {
xMaxValue = curveMaxX;
}
}
return xMaxValue;
}
/**
* @return maximum visible X axis value */
public double getMaxXAxisVisibleValue() {
// no widgets in panel
assert layoutPanel.getWidgetCount() > 0;
SimplePlot plot = layoutPanel.getFirstChild().getPlotRepresentation().getSimplePlot();
return plot.getAxes().getX().getMaximumValue();
}
/**
* @return minimum visible X axis value */
public double getMinXAxisVisibleValue() {
// no widgets in panel
assert layoutPanel.getWidgetCount() > 0;
SimplePlot plot = layoutPanel.getFirstChild().getPlotRepresentation().getSimplePlot();
return plot.getAxes().getX().getMinimumValue();
}
/**
* Change layout of plots (single columns, two columns) */
public void changeLayout() {
layoutPanel.changeLayout(layoutPanel.getLayout().getNext());
}
/**
* Change height of plots */
public void changeChildrenHeight(Integer height) {
layoutPanel.changeChildrenHeight(height);
}
/**
* Scroll to bottom of layout panel (panel with plots) */
public void scrollToBottom() {
scrollPanelMetrics.scrollToBottom();
}
}