/*******************************************************************************
* Copyright 2009, 2010 Lars Grammel
*
* 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 org.thechiselgroup.choosel.visualization_component.chart.client.barchart;
import java.util.Comparator;
import org.thechiselgroup.choosel.core.client.resources.DataType;
import org.thechiselgroup.choosel.core.client.ui.Colors;
import org.thechiselgroup.choosel.core.client.ui.TextBoundsEstimator;
import org.thechiselgroup.choosel.core.client.util.collections.LightweightCollection;
import org.thechiselgroup.choosel.core.client.views.SidePanelSection;
import org.thechiselgroup.choosel.core.client.views.filter.GreaterThanSlotValuePredicate;
import org.thechiselgroup.choosel.core.client.views.model.Slot;
import org.thechiselgroup.choosel.core.client.views.model.ViewContentDisplayProperty;
import org.thechiselgroup.choosel.core.client.views.model.ViewItem;
import org.thechiselgroup.choosel.core.client.views.model.ViewItem.Status;
import org.thechiselgroup.choosel.core.client.views.model.ViewItem.Subset;
import org.thechiselgroup.choosel.core.client.views.sorting.ViewItemDoubleComparator;
import org.thechiselgroup.choosel.protovis.client.PV;
import org.thechiselgroup.choosel.protovis.client.PVAlignment;
import org.thechiselgroup.choosel.protovis.client.PVBar;
import org.thechiselgroup.choosel.protovis.client.PVEventHandler;
import org.thechiselgroup.choosel.protovis.client.PVLabel;
import org.thechiselgroup.choosel.protovis.client.PVLinearScale;
import org.thechiselgroup.choosel.protovis.client.PVMark;
import org.thechiselgroup.choosel.protovis.client.PVPanel;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsArgs;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsBooleanFunction;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsDoubleFunction;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsStringFunction;
import org.thechiselgroup.choosel.visualization_component.chart.client.ChartViewContentDisplay;
import org.thechiselgroup.choosel.visualization_component.chart.client.functions.DecimalFormattedSlotResolver;
import org.thechiselgroup.choosel.visualization_component.chart.client.functions.TickFormatFunction;
import org.thechiselgroup.choosel.visualization_component.chart.client.functions.ViewItemColorSlotAccessor;
import org.thechiselgroup.choosel.visualization_component.chart.client.functions.ViewItemPredicateJsBooleanFunction;
import org.thechiselgroup.choosel.visualization_component.chart.client.functions.ViewItemStringSlotAccessor;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
/* TODO refactor such that the differences between vertical and horizontal bar chart
* are extracted and the commonalities are kept.
*/
// TODO right side ticks
// TODO leverage scales
public class BarChart extends ChartViewContentDisplay {
private class BarSpacingProperty implements
ViewContentDisplayProperty<Boolean> {
@Override
public String getPropertyName() {
return BAR_SPACING_PROPERTY;
}
@Override
public Boolean getValue() {
return getBarSpacing();
}
@Override
public void setValue(Boolean value) {
setBarSpacing(value);
}
}
private class LayoutProperty implements
ViewContentDisplayProperty<LayoutType> {
@Override
public String getPropertyName() {
return LAYOUT_PROPERTY;
}
@Override
public LayoutType getValue() {
return getLayout();
}
@Override
public void setValue(LayoutType value) {
setLayout(value);
}
}
public static enum LayoutType {
VERTICAL("Vertical"), HORIZONTAL("Horizontal"), AUTOMATIC("Automatic");
private String name;
LayoutType(String name) {
this.name = name;
}
/**
* @return space that is available for the bar height.
*/
private double getBarLengthSpace(int chartHeight, int chartWidth) {
return isVerticalBarChart(chartHeight, chartWidth) ? chartHeight
: chartWidth;
}
/**
* @return space that is available for the bar width.
*/
private double getBarWidthSpace(int chartHeight, int chartWidth) {
return isVerticalBarChart(chartHeight, chartWidth) ? chartWidth
: chartHeight;
}
public String getName() {
return name;
}
private boolean isVerticalBarChart(int chartHeight, int chartWidth) {
return this == LayoutType.VERTICAL
|| (this == LayoutType.AUTOMATIC && chartHeight < chartWidth);
}
}
private class ThinPartialBarsProperty implements
ViewContentDisplayProperty<Boolean> {
@Override
public String getPropertyName() {
return THIN_PARTIAL_BARS_PROPERTY;
}
@Override
public Boolean getValue() {
return getThinPartialBars();
}
@Override
public void setValue(Boolean value) {
setThinPartialBars(value);
}
}
private class ValueLabelVisibilityProperty implements
ViewContentDisplayProperty<Boolean> {
@Override
public String getPropertyName() {
return VALUE_LABEL_VISIBILITY_PROPERTY;
}
@Override
public Boolean getValue() {
return getValueLabelVisibility();
}
@Override
public void setValue(Boolean value) {
setValueLabelVisibility(value);
}
}
// TODO move to protovis (events)
private static final String ALL = "all";
// TODO move to protovis (cursor)
private static final String POINTER = "pointer";
public final static String ID = "org.thechiselgroup.choosel.visualization_component.chart.BarChart";
public static final Slot BAR_LABEL = new Slot("barLabel", "Label",
DataType.TEXT);
public static final Slot BAR_LENGTH = new Slot("barLength", "Bar Length",
DataType.NUMBER);
public static final Slot BAR_COLOR = new Slot("barColor", "Color",
DataType.COLOR);
public static final Slot BAR_BORDER_COLOR = new Slot("barBorderColor",
"Border Color", DataType.COLOR);
public static final Slot PARTIAL_BAR_LENGTH = new Slot("partialBarLength",
"Partial Bar Length", DataType.NUMBER);
public static final Slot PARTIAL_BAR_COLOR = new Slot("partialBarColor",
"Partial Bar Color", DataType.COLOR);
public static final Slot PARTIAL_BAR_BORDER_COLOR = new Slot(
"partialBarBorderColor", "Partial Bar Border Color", DataType.COLOR);
public static final Slot[] SLOTS = new Slot[] { BAR_LABEL, BAR_LENGTH,
BAR_COLOR, BAR_BORDER_COLOR, PARTIAL_BAR_LENGTH, PARTIAL_BAR_COLOR,
PARTIAL_BAR_BORDER_COLOR };
public static final String LAYOUT_PROPERTY = "layout";
public static final String BAR_SPACING_PROPERTY = "barSpacing";
public static final String VALUE_LABEL_VISIBILITY_PROPERTY = "valueLabelVisibility";
public static final String THIN_PARTIAL_BARS_PROPERTY = "thinPartialBars";
private static final int BORDER_BOTTOM = 35;
private static final int BORDER_LEFT = 5;
private static final int BORDER_TOP = 5;
private static final int BORDER_RIGHT = 5;
private static final String GRIDLINE_SCALE_COLOR = "rgba(255,255,255,.3)";
private static final String AXIS_SCALE_COLOR = Colors.GRAY_1;
private static final double BAR_STROKE_WIDTH = 0.5d;
private static final String FONT_WEIGHT = "normal";
private static final String FONT_SIZE = "10px";
private static final String FONT_STYLE = "normal";
private static final String FONT_FAMILY = "sans-serif";
private static final String FONT = FONT_SIZE + " " + FONT_FAMILY;
private double[] regularValues;
private boolean valueLabelVisibility = true;
// TODO semantic meaning (bar length etc) --> makes different settings
// easier
protected int chartHeight;
protected int chartWidth;
protected JsStringFunction partialLabelText = new DecimalFormattedSlotResolver(
PARTIAL_BAR_LENGTH, 2);
private JsStringFunction fullMarkLabelText = new DecimalFormattedSlotResolver(
BAR_LENGTH, 2);
private JsBooleanFunction showPartialBars = new ViewItemPredicateJsBooleanFunction(
new GreaterThanSlotValuePredicate(PARTIAL_BAR_LENGTH, 0));
private JsDoubleFunction barStart = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
return calculateBarStart(args.<PVMark> getThis().index());
}
};
private JsDoubleFunction partialBarStart = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
PVMark _this = args.getThis();
double regularBarStart = calculateBarStart(_this.index());
return (partialBarThinner) ? regularBarStart + calculateBarWidth()
* 0.33 : regularBarStart;
}
};
/**
* Calculates the length of the highlighted bar.
*/
private JsDoubleFunction partialBarLength = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
return calculatePartialBarLength(args.<ViewItem> getObject())
- BAR_STROKE_WIDTH; // subtract initial offset
}
};
private JsDoubleFunction regularBarLength = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
PVMark _this = args.getThis();
return calculateBarLength(regularValues[_this.index()])
- BAR_STROKE_WIDTH; // subtract initial offset
}
};
private JsDoubleFunction barWidth = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
return calculateBarWidth();
}
};
private JsDoubleFunction partialBarWidth = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
return partialBarThinner ? 0.33 * calculateBarWidth()
: calculateBarWidth();
}
};
private JsStringFunction scaleStrokeStyle = new JsStringFunction() {
@Override
public String f(JsArgs args) {
double d = args.getDouble();
return d == 0 ? AXIS_SCALE_COLOR : GRIDLINE_SCALE_COLOR;
}
};
private PVBar regularBar;
private PVBar partialBar;
private JsDoubleFunction baselineLabelStart = new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
PVMark _this = args.getThis();
return calculateBarStart(_this.index()) + calculateBarWidth() / 2;
}
};
private final static String BAR_TEXT_BASELINE = PVAlignment.TOP;
private LayoutType layout = LayoutType.HORIZONTAL;
protected JsStringFunction barValueLabelTextStyle = new JsStringFunction() {
@Override
public String f(JsArgs args) {
ViewItem viewItem = args.<ViewItem> getObject();
PVMark _this = args.getThis();
if (Status.FULL.equals(viewItem.getStatus(Subset.HIGHLIGHTED))) {
return Colors.BLACK;
}
// XXX calculate label size instead of using 60px
if (calculateBarLength(regularValues[_this.index()]) < 60) {
return Colors.GRAY_2;
}
return Colors.WHITE;
}
};
private String valueAxisLabel;
private JsStringFunction valueAxisLabelFunction = new JsStringFunction() {
@Override
public String f(JsArgs args) {
return valueAxisLabel;
}
};
private JsStringFunction valueLabelAlignment = new JsStringFunction() {
@Override
public String f(JsArgs args) {
// XXX pre-calculation should be done by methods in
// chart..
// XXX calculate label size instead of taking 60px
PVMark _this = args.getThis();
if (calculateBarLength(regularValues[_this.index()]) < 60) {
return PVAlignment.LEFT;
}
return PVAlignment.RIGHT;
}
};
protected JsStringFunction regularMarkLabelText = new JsStringFunction() {
@Override
public String f(JsArgs args) {
ViewItem viewItem = args.getObject();
// TODO separate visibility determination
return viewItem.getValueAsDouble(BAR_LENGTH)
- viewItem.getValueAsDouble(PARTIAL_BAR_LENGTH) < 1 ? null
: Double.toString(viewItem.getValueAsDouble(BAR_LENGTH)
- viewItem.getValueAsDouble(PARTIAL_BAR_LENGTH));
}
};
protected JsStringFunction partialBarValueLabelText = new JsStringFunction() {
@Override
public String f(JsArgs args) {
ViewItem viewItem = args.getObject();
// TODO separate visibility determination
return viewItem.getValueAsDouble(PARTIAL_BAR_LENGTH) <= 0 ? null
: Double.toString(viewItem
.getValueAsDouble(PARTIAL_BAR_LENGTH));
}
};
protected double maxChartItemValue;
private boolean barSpacing = true;
private Comparator<ViewItem> viewItemComparator = new ViewItemDoubleComparator(
BAR_LENGTH);
private PVLabel barLabel;
private boolean partialBarThinner = false;
/**
* We use an invisible interaction bar to capture mouse events over the
* bars. Without this bar, the grid lines and the partial bars lead to
* flickering and inconsistencies due to automatically fired events.
*/
private PVPanel invisibleInteractionBar;
public BarChart() {
registerProperty(new LayoutProperty());
registerProperty(new BarSpacingProperty());
registerProperty(new ValueLabelVisibilityProperty());
registerProperty(new ThinPartialBarsProperty());
}
@Override
protected void beforeRender() {
super.beforeRender();
viewItemsJsArray.sortStable(getViewItemComparator());
calculateMaximumChartItemValue();
if (viewItemsJsArray.length() == 0) {
return;
}
regularValues = new double[viewItemsJsArray.length()];
for (int i = 0; i < viewItemsJsArray.length(); i++) {
regularValues[i] = viewItemsJsArray.get(i).getValueAsDouble(
BAR_LENGTH);
}
}
@Override
public void buildChart() {
assert viewItemsJsArray.length() >= 1;
// TODO do we need sorting?
// Collections.sort(chartItems, new ChartItemComparator(
// SlotResolver.CHART_LABEL_SLOT));
calculateChartVariables();
calculateMaximumChartItemValue();
if (layout.isVerticalBarChart(chartHeight, chartWidth)) {
getChart().left(BORDER_LEFT + 40).bottom(BORDER_BOTTOM);
// TODO axis label
drawVerticalBarChart();
drawVerticalBarScales();
} else {
drawHorizontalBarChart();
}
getChart().add(PV.Rule).bottom(0).left(0).width(chartWidth)
.strokeStyle(AXIS_SCALE_COLOR).lineWidth(BAR_STROKE_WIDTH);
getChart().add(PV.Rule).left(0).bottom(0).height(chartHeight)
.strokeStyle(AXIS_SCALE_COLOR).lineWidth(BAR_STROKE_WIDTH);
}
private double calculateBarLength(double value) {
return (value * getBarLengthSpace() / maxChartItemValue);
}
private double calculateBarStart(int index) {
double barAreaStart = index * getBarWidthSpace()
/ viewItemsJsArray.length();
double barOffset = barSpacing ? calculateBarWidth() / 2 : 0;
return barAreaStart + barOffset;
}
private double calculateBarWidth() {
double spacePerBar = getBarWidthSpace() / viewItemsJsArray.length();
if (barSpacing) {
spacePerBar /= 2;
}
return spacePerBar;
}
private void calculateChartVariables() {
if (layout.isVerticalBarChart(chartHeight, chartWidth)) {
chartWidth = width - BORDER_LEFT - 40 - BORDER_RIGHT;
} else {
chartWidth = width - BORDER_LEFT - BORDER_RIGHT
- calculateHorizontalLabelSpace();
}
chartHeight = height - BORDER_BOTTOM - BORDER_TOP;
}
private int calculateHorizontalLabelSpace() {
TextBoundsEstimator estimator = new TextBoundsEstimator();
estimator.applyFontSettings(FONT_FAMILY, FONT_STYLE, FONT_WEIGHT,
FONT_SIZE);
// max over widths for labels
int maxWidth = 0;
for (int i = 0; i < viewItemsJsArray.length(); i++) {
String label = viewItemsJsArray.get(i).getValue(BAR_LABEL);
estimator.setText(label);
int width = estimator.getWidth();
if (maxWidth < width) {
maxWidth = width;
}
}
return maxWidth;
}
protected void calculateMaximumChartItemValue() {
maxChartItemValue = 0;
for (int i = 0; i < viewItemsJsArray.length(); i++) {
double currentItemValue = viewItemsJsArray.get(i).getValueAsDouble(
BAR_LENGTH);
if (maxChartItemValue < currentItemValue) {
maxChartItemValue = currentItemValue;
}
}
}
private double calculatePartialBarLength(ViewItem d) {
return calculateBarLength(d.getValueAsDouble(PARTIAL_BAR_LENGTH));
}
private void drawHorizontalBarChart() {
getChart().left(BORDER_LEFT + calculateHorizontalLabelSpace()).bottom(
BORDER_BOTTOM);
drawHorizontalMeasurementAxis();
/*
* The stroke gets added to the length, but is part of the visible
* appearance. We thus have to adjust the bar length and position for
* the stroke width.
*
* The regular bar starts after the partial bar. Otherwise there are
* color differences if the partial bar is semi-transparent.
*/
regularBar = getChart().add(PV.Bar).data(viewItemsJsArray)
.left(BAR_STROKE_WIDTH).width(regularBarLength)
.bottom(barStart).height(barWidth)
.fillStyle(new ViewItemColorSlotAccessor(BAR_COLOR))
.strokeStyle(new ViewItemColorSlotAccessor(BAR_BORDER_COLOR))
.lineWidth(BAR_STROKE_WIDTH);
if (valueLabelVisibility) {
regularBar.anchor(PVAlignment.RIGHT).add(PV.Label)
.textBaseline(PVAlignment.MIDDLE).text(fullMarkLabelText)
.textStyle(barValueLabelTextStyle)
.textAlign(valueLabelAlignment);
}
// TODO negative bars (in opposite direction)
/*
* Partial bars have a white bar below them to prevent the regular bar
* from affecting a semi-transparent partial bar.
*/
getChart().add(PV.Bar).data(viewItemsJsArray).left(BAR_STROKE_WIDTH)
.width(partialBarLength).bottom(partialBarStart)
.height(partialBarWidth).fillStyle(Colors.WHITE)
.strokeStyle(Colors.WHITE).lineWidth(BAR_STROKE_WIDTH)
.visible(showPartialBars);
partialBar = getChart()
.add(PV.Bar)
.data(viewItemsJsArray)
.left(BAR_STROKE_WIDTH)
.width(partialBarLength)
.bottom(partialBarStart)
.height(partialBarWidth)
.fillStyle(new ViewItemColorSlotAccessor(PARTIAL_BAR_COLOR))
.strokeStyle(
new ViewItemColorSlotAccessor(PARTIAL_BAR_BORDER_COLOR))
.lineWidth(BAR_STROKE_WIDTH).visible(showPartialBars);
if (valueLabelVisibility) {
partialBar.anchor(PVAlignment.RIGHT).add(PV.Label)
.textBaseline(BAR_TEXT_BASELINE).text(partialLabelText)
.textStyle(Colors.BLACK).textBaseline(PVAlignment.MIDDLE);
}
drawHorizontalGridLines();
invisibleInteractionBar = getChart().add(PV.Panel)
.data(viewItemsJsArray).left(BAR_STROKE_WIDTH)
.width(regularBarLength).bottom(barStart).height(barWidth)
.lineWidth(BAR_STROKE_WIDTH).cursor(POINTER).events(ALL);
barLabel = getChart().add(PV.Label).data(viewItemsJsArray)
.bottom(baselineLabelStart).textAlign(PVAlignment.RIGHT)
.left(0).font(FONT)
.text(new ViewItemStringSlotAccessor(BAR_LABEL))
.textBaseline(PVAlignment.MIDDLE).events(ALL).cursor(POINTER);
}
private void drawHorizontalGridLines() {
PVLinearScale scale = PV.Scale.linear(0, maxChartItemValue).range(0,
chartWidth);
getChart().add(PV.Rule).data(scale.ticks(5)).left(scale).bottom(0)
.strokeStyle(scaleStrokeStyle).height(chartHeight)
.anchor(PVAlignment.BOTTOM).add(PV.Label)
.text(new TickFormatFunction(scale));
}
public void drawHorizontalMeasurementAxis() {
getChart().add(PV.Label).bottom(-BORDER_BOTTOM + 5)
.left(chartWidth / 2).text(valueAxisLabelFunction)
.textAlign(PVAlignment.CENTER);
}
private void drawVerticalBarChart() {
/*
* The regular bar starts after the partial bar. Otherwise there are
* color differences if the partial bar is semi-transparent.
*/
regularBar = getChart().add(PV.Bar).data(viewItemsJsArray)
.bottom(BAR_STROKE_WIDTH).height(regularBarLength)
.left(barStart).width(barWidth)
.fillStyle(new ViewItemColorSlotAccessor(BAR_COLOR))
.strokeStyle(new ViewItemColorSlotAccessor(BAR_BORDER_COLOR))
.lineWidth(BAR_STROKE_WIDTH);
if (valueLabelVisibility) {
regularBar.anchor(PVAlignment.TOP).add(PV.Label)
.textAngle(-Math.PI / 2).textBaseline(PVAlignment.MIDDLE)
.textAlign(valueLabelAlignment)
.textStyle(barValueLabelTextStyle).text(fullMarkLabelText);
}
/*
* Partial bars have a white bar below them to prevent the regular bar
* from affecting a semi-transparent partial bar.
*/
getChart().add(PV.Bar).data(viewItemsJsArray).bottom(BAR_STROKE_WIDTH)
.height(partialBarLength).left(partialBarStart)
.width(partialBarWidth).fillStyle(Colors.WHITE)
.strokeStyle(Colors.WHITE).lineWidth(BAR_STROKE_WIDTH)
.visible(showPartialBars);
partialBar = getChart()
.add(PV.Bar)
.data(viewItemsJsArray)
.bottom(BAR_STROKE_WIDTH)
.height(partialBarLength)
.left(partialBarStart)
.width(partialBarWidth)
.fillStyle(new ViewItemColorSlotAccessor(PARTIAL_BAR_COLOR))
.strokeStyle(
new ViewItemColorSlotAccessor(PARTIAL_BAR_BORDER_COLOR))
.lineWidth(BAR_STROKE_WIDTH).visible(showPartialBars);
if (valueLabelVisibility) {
partialBar.anchor(PVAlignment.TOP).add(PV.Label)
.textBaseline(PVAlignment.MIDDLE)
.textAlign(PVAlignment.RIGHT)
.text(partialBarValueLabelText).textAngle(-Math.PI / 2);
}
invisibleInteractionBar = getChart().add(PV.Panel)
.data(viewItemsJsArray).bottom(BAR_STROKE_WIDTH)
.height(regularBarLength).left(barStart).width(barWidth)
.lineWidth(BAR_STROKE_WIDTH).cursor(POINTER).events(ALL);
getChart().add(PV.Label).data(viewItemsJsArray)
.left(baselineLabelStart).textAlign(PVAlignment.CENTER)
.bottom(new JsDoubleFunction() {
@Override
public double f(JsArgs args) {
PVMark _this = args.getThis();
// TODO dynamic positioning depending on label size
if (chartWidth / regularValues.length > 60) {
return -10;
}
return _this.index() % 2 == 0 ? -10 : -25;
}
}).text(new ViewItemStringSlotAccessor(BAR_LABEL))
.textBaseline(PVAlignment.MIDDLE);
}
// TODO extract scale ticks # as property
protected void drawVerticalBarScales() {
PVLinearScale scale = PV.Scale.linear(0, maxChartItemValue).range(0,
chartHeight);
getChart().add(PV.Rule).data(scale.ticks(5)).left(0).bottom(scale)
.strokeStyle(scaleStrokeStyle).width(chartWidth)
.anchor(PVAlignment.LEFT).add(PV.Label)
.text(new TickFormatFunction(scale));
}
private double getBarLengthSpace() {
return layout.getBarLengthSpace(chartHeight, chartWidth);
}
private boolean getBarSpacing() {
return barSpacing;
}
private double getBarWidthSpace() {
return layout.getBarWidthSpace(chartHeight, chartWidth);
}
public LayoutType getLayout() {
return layout;
}
@Override
public String getName() {
return "Bar Chart";
}
@Override
public SidePanelSection[] getSidePanelSections() {
FlowPanel settingsPanel = new FlowPanel();
{
settingsPanel.add(new Label("Chart orientation"));
final ListBox layoutBox = new ListBox(false);
layoutBox.setVisibleItemCount(1);
for (LayoutType layout : LayoutType.values()) {
layoutBox.addItem(layout.getName(), layout.toString());
}
layoutBox.setSelectedIndex(1);
layoutBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
setLayout(LayoutType.valueOf(layoutBox.getValue(layoutBox
.getSelectedIndex())));
}
});
settingsPanel.add(layoutBox);
}
{
settingsPanel.add(new Label("Bar spacing"));
CheckBox checkBox = new CheckBox();
checkBox.setText("separate");
checkBox.setValue(barSpacing);
checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
setBarSpacing(event.getValue());
}
});
settingsPanel.add(checkBox);
}
{
settingsPanel.add(new Label("Value labels"));
CheckBox checkBox = new CheckBox();
checkBox.setText("visible");
checkBox.setValue(valueLabelVisibility);
checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
setValueLabelVisibility(event.getValue());
}
});
settingsPanel.add(checkBox);
}
{
settingsPanel.add(new Label("Partial bar width"));
CheckBox checkBox = new CheckBox();
checkBox.setText("thinner");
checkBox.setValue(partialBarThinner);
checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
setThinPartialBars(event.getValue());
}
});
settingsPanel.add(checkBox);
}
return new SidePanelSection[] { new SidePanelSection("Settings",
settingsPanel), };
}
@Override
public Slot[] getSlots() {
return SLOTS;
}
public boolean getThinPartialBars() {
return partialBarThinner;
}
public boolean getValueLabelVisibility() {
return valueLabelVisibility;
}
public Comparator<ViewItem> getViewItemComparator() {
return viewItemComparator;
}
@Override
protected void registerEventHandler(String eventType, PVEventHandler handler) {
invisibleInteractionBar.event(eventType, handler);
barLabel.event(eventType, handler);
}
public void setBarSpacing(boolean barSpacing) {
if (this.barSpacing == barSpacing) {
return;
}
this.barSpacing = barSpacing;
updateChart(true);
}
public void setLayout(LayoutType layout) {
assert layout != null;
if (this.layout.equals(layout)) {
return;
}
this.layout = layout;
updateChart(true);
}
public void setThinPartialBars(boolean thinner) {
this.partialBarThinner = thinner;
}
public void setValueLabelVisibility(boolean valueLabelVisibility) {
if (this.valueLabelVisibility == valueLabelVisibility) {
return;
}
this.valueLabelVisibility = valueLabelVisibility;
updateChart(true);
}
public void setViewItemComparator(Comparator<ViewItem> viewItemComparator) {
if (this.viewItemComparator == viewItemComparator) {
return;
}
this.viewItemComparator = viewItemComparator;
updateChart(true);
}
@Override
public void update(LightweightCollection<ViewItem> addedResourceItems,
LightweightCollection<ViewItem> updatedResourceItems,
LightweightCollection<ViewItem> removedResourceItems,
LightweightCollection<Slot> changedSlots) {
// TODO re-enable - might be wrong for initial configuration...
// if (!changedSlots.isEmpty()) {
valueAxisLabel = callback.getSlotResolverDescription(BAR_LENGTH);
// }
super.update(addedResourceItems, updatedResourceItems,
removedResourceItems, changedSlots);
}
}