/*******************************************************************************
* Copyright 2011 Antti Havanko
*
* This file is part of Motiver.fi.
* Motiver.fi is licensed under one open source license and one commercial license.
*
* Commercial license: This is the appropriate option if you want to use Motiver.fi in
* commercial purposes. Contact license@motiver.fi for licensing options.
*
* Open source license: This is the appropriate option if you are creating an open source
* application with a license compatible with the GNU GPL license v3. Although the GPLv3 has
* many terms, the most important is that you must provide the source code of your application
* to your users so they can be free to modify your application for their own needs.
******************************************************************************/
package com.delect.motiver.client.view.statistics;
import java.util.List;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.delect.motiver.client.AppController;
import com.delect.motiver.client.Motiver;
import com.delect.motiver.client.presenter.statistics.StatsNutritionDayHistoryPresenter;
import com.delect.motiver.shared.Constants;
import com.delect.motiver.shared.NutritionDayModel;
import com.delect.motiver.shared.util.CommonUtils;
import com.extjs.gxt.charts.client.Chart;
import com.extjs.gxt.charts.client.model.ChartModel;
import com.extjs.gxt.charts.client.model.Legend;
import com.extjs.gxt.charts.client.model.Legend.Position;
import com.extjs.gxt.charts.client.model.LineDataProvider;
import com.extjs.gxt.charts.client.model.Scale;
import com.extjs.gxt.charts.client.model.ScaleProvider;
import com.extjs.gxt.charts.client.model.axis.XAxis;
import com.extjs.gxt.charts.client.model.axis.YAxis;
import com.extjs.gxt.charts.client.model.charts.ChartConfig;
import com.extjs.gxt.charts.client.model.charts.LineChart;
import com.extjs.gxt.charts.client.model.charts.LineChart.LineStyle;
import com.extjs.gxt.charts.client.model.charts.dots.Dot;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelStringProvider;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.Text;
import com.extjs.gxt.ui.client.widget.layout.RowData;
import com.extjs.gxt.ui.client.widget.layout.RowLayout;
public class StatsNutritionDayHistoryView extends StatsNutritionDayHistoryPresenter.StatsNutritionDayHistoryDisplay {
LayoutContainer panelData = new LayoutContainer();
ListStore<NutritionDayModel> store = new ListStore<NutritionDayModel>();
public StatsNutritionDayHistoryView() {
this.setLayout(new RowLayout());
//title
Text textTitle = new Text(AppController.Lang.DaysCalories());
textTitle.setStyleName("label-title-big");
this.add(textTitle, new RowData(-1, -1, new Margins(0, 0, 20, 0)));
panelData.setHeight(500);
this.add(panelData);
}
@Override
public Widget asWidget() {
return this;
}
@Override
public LayoutContainer getBodyContainer() {
return panelData;
}
@Override
public void setDaysData(List<NutritionDayModel> values) {
try {
getBodyContainer().removeAll();
getBodyContainer().layout();
if(values == null) {
return;
}
//if only single value -> show as text
if(values.size() == 1) {
VerticalPanel panelSingle = new VerticalPanel();
panelSingle.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
panelSingle.setSpacing(10);
panelSingle.setWidth("100%");
Text text = new Text(AppController.Lang.OnlyOneValueFound() + ":");
text.setStyleName("label-title-small");
panelSingle.add(text);
//value
NutritionDayModel model = values.get(0);
Text textValue = new Text();
textValue.setText(CommonUtils.getDateString(model.getDate(), true, true) + ": " +
"<b>" + AppController.Lang.Energy() + ":</b> " + (int)model.getEnergy() + "kcal | " +
"<b>" + AppController.Lang.Protein() + ":</b> " + (int)model.getProtein() + "g | " +
"<b>" + AppController.Lang.Carbohydrates() + ":</b> " + (int)model.getCarb() + "g | " +
"<b>" + AppController.Lang.Fet() + ":</b> " + (int)model.getFet() + "g");
panelSingle.add(textValue);
getBodyContainer().add(panelSingle, new RowData(-1, -1, new Margins(100, 0, 100, 0)));
getBodyContainer().layout();
}
//show in graph
else {
store.removeAll();
store.add(values);
//show graph
final Chart chart = new Chart(Constants.URL_APP_STATIC + "resources/chart/open-flash-chart.swf");
chart.setHeight(500);
chart.setBorders(true);
chart.setChartModel(getChartData());
getBodyContainer().add(chart);
getBodyContainer().layout();
}
} catch (Exception e) {
Motiver.showException(e);
}
}
private ChartModel getChartData() {
ChartModel model = new ChartModel();
model.setBackgroundColour("-1");
model.setLegend(new Legend(Position.TOP, true));
model.setScaleProvider(new ScaleProvider() {
@Override
public Scale calcScale(double min, double max) {
if (min == 0 && max == 0) {
return new Scale(-1, 1, 1);
}
min = (int)(min * ((min > 0) ? 0.95 : 1.05));
max = (int)(max * ((max > 0) ? 1.05 : 0.95)) + 1;
//round to closest five
min -= min % 5;
max += (5 - max % 5);
double interval = 40;
double diff = Math.abs(max - min);
if(diff <= 600) {
interval = 100;
}
else if(diff <= 1200) {
interval = 200;
}
else {
double i = diff / 5;
long l = Math.round(i / 100);
interval = l * 100;
}
return new Scale(min, max, interval);
}
});
model.setScaleProviderRightAxis(new ScaleProvider() {
@Override
public Scale calcScale(double min, double max) {
if (min == 0 && max == 0) {
return new Scale(-1, 1, 1);
}
min = (int)(min * ((min > 0) ? 0.95 : 1.05));
max = (int)(max * ((max > 0) ? 1.05 : 0.95)) + 1;
//round to closest five
min -= min % 5;
max += (5 - max % 5);
double interval = 4;
double diff = Math.abs(max - min);
if(diff <= 60) {
interval = 10;
}
else if(diff <= 120) {
interval = 20;
}
else {
interval = (int)(diff / 2.5);
}
return new Scale(min, max, interval);
}
});
//protein
LineChart lineP = new LineChart();
lineP.setWidth(2);
lineP.setLineStyle(new LineStyle(10, 10));
lineP.setColour(Constants.COLOR_GRAPH[1]);
LineDataProvider lineProviderP = new LineDataProvider("p") {
@Override
public void populateData(ChartConfig config) {
LineChart chart = (LineChart) config;
chart.getValues().clear();
XAxis xAxis = null;
if (labelProperty != null || labelProvider != null) {
xAxis = chart.getModel().getXAxis();
if (xAxis == null) {
xAxis = new XAxis();
chart.getModel().setXAxis(xAxis);
}
xAxis.getLabels().getLabels().clear();
}
boolean first = true;
for (ModelData m : store.getModels()) {
Number value = getValue(m);
if (value == null) {
chart.addNullValue();
} else {
Dot dot = new Dot();
dot.setValue(value);
dot.setTooltip(AppController.Lang.Protein() + ": " + value.intValue() + "g (" + CommonUtils.getDateString(((NutritionDayModel)m).getDate(), true, true) + ")");
chart.addDots(dot);
maxYValue = first ? value.doubleValue() : Math.max(maxYValue, value.doubleValue());
minYValue = first ? value.doubleValue() : Math.min(minYValue, value.doubleValue());
first = false;
}
if (xAxis != null) {
xAxis.addLabels(getLabel(m));
}
}
}
};
lineProviderP.bind(store);
lineP.setDataProvider(lineProviderP);
lineP.setRightAxis(true);
model.addChartConfig(lineP);
//carbs
LineChart lineC = new LineChart();
lineC.setWidth(2);
lineC.setLineStyle(new LineStyle(10, 10));
lineC.setColour(Constants.COLOR_GRAPH[2]);
LineDataProvider lineProviderC = new LineDataProvider("c") {
@Override
public void populateData(ChartConfig config) {
LineChart chart = (LineChart) config;
chart.getValues().clear();
XAxis xAxis = null;
if (labelProperty != null || labelProvider != null) {
xAxis = chart.getModel().getXAxis();
if (xAxis == null) {
xAxis = new XAxis();
chart.getModel().setXAxis(xAxis);
}
xAxis.getLabels().getLabels().clear();
}
boolean first = true;
for (ModelData m : store.getModels()) {
Number value = getValue(m);
if (value == null) {
chart.addNullValue();
} else {
Dot dot = new Dot();
dot.setValue(value);
dot.setTooltip(AppController.Lang.Carbohydrates() + ": " + value.intValue() + "g (" + CommonUtils.getDateString(((NutritionDayModel)m).getDate(), true, true) + ")");
chart.addDots(dot);
// chart.addValues(n);
maxYValue = first ? value.doubleValue() : Math.max(maxYValue, value.doubleValue());
minYValue = first ? value.doubleValue() : Math.min(minYValue, value.doubleValue());
first = false;
}
if (xAxis != null) {
xAxis.addLabels(getLabel(m));
}
}
}
};
lineProviderC.bind(store);
lineC.setDataProvider(lineProviderC);
lineC.setRightAxis(true);
model.addChartConfig(lineC);
//fet
LineChart lineF = new LineChart();
lineF.setWidth(2);
lineF.setLineStyle(new LineStyle(10, 10));
lineF.setColour(Constants.COLOR_GRAPH[3]);
LineDataProvider lineProviderF = new LineDataProvider("f") {
@Override
public void populateData(ChartConfig config) {
LineChart chart = (LineChart) config;
chart.getValues().clear();
XAxis xAxis = null;
if (labelProperty != null || labelProvider != null) {
xAxis = chart.getModel().getXAxis();
if (xAxis == null) {
xAxis = new XAxis();
chart.getModel().setXAxis(xAxis);
}
xAxis.getLabels().getLabels().clear();
}
boolean first = true;
for (ModelData m : store.getModels()) {
Number value = getValue(m);
if (value == null) {
chart.addNullValue();
} else {
Dot dot = new Dot();
dot.setValue(value);
dot.setTooltip(AppController.Lang.Fet() + ": " + value.intValue() + "g (" + CommonUtils.getDateString(((NutritionDayModel)m).getDate(), true, true) + ")");
chart.addDots(dot);
// chart.addValues(n);
maxYValue = first ? value.doubleValue() : Math.max(maxYValue, value.doubleValue());
minYValue = first ? value.doubleValue() : Math.min(minYValue, value.doubleValue());
first = false;
}
if (xAxis != null) {
xAxis.addLabels(getLabel(m));
}
}
}
};
lineProviderF.bind(store);
lineF.setDataProvider(lineProviderF);
lineF.setRightAxis(true);
model.addChartConfig(lineF);
//energy
LineChart line = new LineChart();
line.setWidth(6);
line.setColour(Constants.COLOR_GRAPH[0]);
LineDataProvider lineProvider = new LineDataProvider("e") {
@Override
public void populateData(ChartConfig config) {
LineChart chart = (LineChart) config;
chart.getValues().clear();
XAxis xAxis = null;
if (labelProperty != null || labelProvider != null) {
xAxis = chart.getModel().getXAxis();
if (xAxis == null) {
xAxis = new XAxis();
chart.getModel().setXAxis(xAxis);
}
xAxis.getLabels().getLabels().clear();
}
boolean first = true;
for (ModelData m : store.getModels()) {
Number n = getValue(m);
if (n == null) {
chart.addNullValue();
} else {
Dot dot = new Dot();
dot.setValue(n);
dot.setTooltip(AppController.Lang.Energy() + ": " + n.intValue() + "kcal (" + CommonUtils.getDateString(((NutritionDayModel)m).getDate(), true, true) + ")");
chart.addDots(dot);
// chart.addValues(n);
maxYValue = first ? n.doubleValue() : Math.max(maxYValue, n.doubleValue());
minYValue = first ? n.doubleValue() : Math.min(minYValue, n.doubleValue());
first = false;
}
if (xAxis != null) {
xAxis.addLabels(getLabel(m));
}
}
}
};
lineProvider.setLabelProvider(new ModelStringProvider<ModelData>() {
@Override
public String getStringValue(ModelData model, String property) {
try {
//don't show labels if too many values
if(store.getModels().size() * 43 <= (getWidth() - 25)) {
NutritionDayModel m = (NutritionDayModel)model;
return CommonUtils.getDateString(m.getDate(), false, true, true);
}
} catch (Exception e) {
Motiver.showException(e);
}
return "";
}
});
lineProvider.bind(store);
line.setDataProvider(lineProvider);
model.addChartConfig(line);
YAxis axis = new YAxis();
model.setYAxisRight(axis);
return model;
}
}