package name.abuchen.portfolio.ui.util.chart;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.swtchart.Chart;
import org.swtchart.IAxis;
import org.swtchart.IBarSeries;
import org.swtchart.ILineSeries;
import org.swtchart.ISeries;
import name.abuchen.portfolio.ui.Messages;
import name.abuchen.portfolio.ui.util.Colors;
public class TimelineChartToolTip extends AbstractChartToolTip
{
private String dateFormat = "%tF"; //$NON-NLS-1$
private DecimalFormat valueFormat = new DecimalFormat("#,##0.00"); //$NON-NLS-1$
private boolean categoryEnabled = false;
private boolean reverseLabels = false;
public TimelineChartToolTip(Chart chart)
{
super(chart);
}
public void enableCategory(boolean enabled)
{
categoryEnabled = enabled;
}
public void reverseLabels(boolean reverseLabels)
{
this.reverseLabels = reverseLabels;
}
public void setDateFormat(String dateFormat)
{
this.dateFormat = dateFormat;
}
public void setValueFormat(DecimalFormat valueFormat)
{
this.valueFormat = valueFormat;
}
@Override
protected Object getFocusObjectAt(Event event)
{
return categoryEnabled ? getFocusCategoryAt(event) : getFocusDateAt(event);
}
private Integer getFocusCategoryAt(Event event)
{
IAxis xAxis = getChart().getAxisSet().getXAxes()[0];
int coordinate = (int) xAxis.getDataCoordinate(event.x);
String[] categories = xAxis.getCategorySeries();
if (coordinate < 0)
coordinate = 0;
else if (coordinate > categories.length - 1)
coordinate = categories.length - 1;
return coordinate;
}
private Date getFocusDateAt(Event event)
{
IAxis xAxis = getChart().getAxisSet().getXAxes()[0];
long time = (long) xAxis.getDataCoordinate(event.x);
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
ISeries[] allSeries = getChart().getSeriesSet().getSeries();
if (allSeries.length == 0)
return null;
ISeries timeSeries = allSeries[0];
int line = Arrays.binarySearch(timeSeries.getXDateSeries(), cal.getTime());
if (line >= 0)
return cal.getTime();
// otherwise: find closest existing date
line = -line - 1;
if (line == 0)
return timeSeries.getXDateSeries()[line];
int length = timeSeries.getXDateSeries().length;
if (line >= length)
return timeSeries.getXDateSeries()[length - 1];
// check which date is closer to the targeted date
long target = cal.getTimeInMillis();
Date left = timeSeries.getXDateSeries()[line - 1];
Date right = timeSeries.getXDateSeries()[line];
return target - left.getTime() < right.getTime() - target ? left : right;
}
@Override
protected void createComposite(Composite parent)
{
final Composite container = new Composite(parent, SWT.NONE);
container.setBackgroundMode(SWT.INHERIT_FORCE);
GridLayoutFactory.swtDefaults().numColumns(2).applyTo(container);
Color foregroundColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
container.setForeground(foregroundColor);
Color backgroundColor = new Color(container.getDisplay(), Colors.INFO_TOOLTIP_BACKGROUND.swt());
container.addDisposeListener(e -> backgroundColor.dispose());
container.setBackground(backgroundColor);
Label left = new Label(container, SWT.NONE);
left.setForeground(foregroundColor);
left.setText(Messages.ColumnDate);
Label right = new Label(container, SWT.NONE);
right.setForeground(foregroundColor);
right.setText(categoryEnabled ? getChart().getAxisSet().getXAxis(0).getCategorySeries()[(Integer) getFocusedObject()]
: String.format(dateFormat, getFocusedObject()));
ISeries[] allSeries = getChart().getSeriesSet().getSeries();
if (reverseLabels)
Collections.reverse(Arrays.asList(allSeries));
for (ISeries series : allSeries)
{
double value;
if (categoryEnabled)
{
int line = (Integer) getFocusedObject();
if (line >= series.getYSeries().length)
continue;
value = series.getYSeries()[line];
}
else
{
int line = Arrays.binarySearch(series.getXDateSeries(), getFocusedObject());
if (line < 0)
continue;
value = series.getYSeries()[line];
}
Color color = series instanceof ILineSeries ? ((ILineSeries) series).getLineColor() : ((IBarSeries) series)
.getBarColor();
left = new Label(container, SWT.NONE);
left.setBackground(color);
left.setForeground(Colors.getTextColor(color));
left.setText(series.getId());
GridDataFactory.fillDefaults().grab(true, false).applyTo(left);
right = new Label(container, SWT.RIGHT);
right.setForeground(foregroundColor);
right.setText(valueFormat.format(value));
GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).applyTo(right);
}
}
}