charts: Add dialog for making a custom chart
authorGabriel-Andrew Pollo-Guilbert <gabrielpolloguilbert@gmail.com>
Fri, 15 Jul 2016 19:49:12 +0000 (15:49 -0400)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Thu, 2 Mar 2017 19:42:10 +0000 (14:42 -0500)
This patch adds a dialog that allow to configure a chart
based on the possible data descriptors of a data
provider.

Change-Id: Ia790b11083ef07b49953cf9f3578eeaea7785495
Signed-off-by: Gabriel-Andrew Pollo-Guilbert <gabrielpolloguilbert@gmail.com>
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/77162
Reviewed-by: Hudson CI
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
13 files changed:
tmf/org.eclipse.tracecompass.tmf.chart.ui/META-INF/MANIFEST.MF
tmf/org.eclipse.tracecompass.tmf.chart.ui/build.properties
tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/barchart.png [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/scatterchart.png [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/provisional/tmf/chart/ui/dialog/ChartMakerDialog.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/provisional/tmf/chart/ui/dialog/package-info.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/dialog/Messages.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/dialog/messages.properties [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/swtchart/SwtScatterChart.java
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/BarChartTypeDefinition.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/IChartTypeDefinition.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/ScatterChartTypeDefinition.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/package-info.java [new file with mode: 0644]

index 0822d32d02d4085c4bfa56770bbba338c518d2c9..0066cefc6f06a052487465b3bf296b85c6ad8059 100644 (file)
@@ -16,12 +16,15 @@ Require-Bundle: org.eclipse.ui,
  org.eclipse.tracecompass.tmf.core,
  org.eclipse.tracecompass.tmf.ui
 Export-Package: org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.chart;x-internal:=true,
+ org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.dialog;x-internal:=true,
  org.eclipse.tracecompass.internal.tmf.chart.ui;x-internal:=true,
  org.eclipse.tracecompass.internal.tmf.chart.ui.aggregator;x-internal:=true,
  org.eclipse.tracecompass.internal.tmf.chart.ui.consumer;x-internal:=true,
  org.eclipse.tracecompass.internal.tmf.chart.ui.data;x-friends:="org.eclipse.tracecompass.tmf.chart.ui.tests",
+ org.eclipse.tracecompass.internal.tmf.chart.ui.dialog;x-internal:=true,
  org.eclipse.tracecompass.internal.tmf.chart.ui.format;x-friends:="org.eclipse.tracecompass.tmf.chart.ui.tests",
- org.eclipse.tracecompass.internal.tmf.chart.ui.swtchart;x-internal:=true
+ org.eclipse.tracecompass.internal.tmf.chart.ui.swtchart;x-internal:=true,
+ org.eclipse.tracecompass.internal.tmf.chart.ui.type;x-internal:=true
 Import-Package: com.google.common.collect,
  org.apache.commons.lang3,
  org.swtchart
index e3a73540d8e426d6223a0755ed7e46a3e4fbc08b..32bf55342f4428dce7ce177bfc53b36a3ed109de 100644 (file)
@@ -13,7 +13,8 @@ bin.includes = META-INF/,\
                .,\
                about.html,\
                plugin.properties,\
-               build.properties
+               build.properties,\
+               icons/
 src.includes = about.html
 additional.bundles = org.eclipse.jdt.annotation
 jars.extra.classpath = platform:/plugin/org.eclipse.jdt.annotation
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/barchart.png b/tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/barchart.png
new file mode 100644 (file)
index 0000000..b9fd370
Binary files /dev/null and b/tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/barchart.png differ
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/scatterchart.png b/tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/scatterchart.png
new file mode 100644 (file)
index 0000000..b13e690
Binary files /dev/null and b/tmf/org.eclipse.tracecompass.tmf.chart.ui/icons/scatterchart.png differ
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/provisional/tmf/chart/ui/dialog/ChartMakerDialog.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/provisional/tmf/chart/ui/dialog/ChartMakerDialog.java
new file mode 100644 (file)
index 0000000..627d997
--- /dev/null
@@ -0,0 +1,895 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.dialog;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnPixelData;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableEditor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartData;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartSeries;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartType;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.IDataChartDescriptor;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.model.IDataChartProvider;
+import org.eclipse.tracecompass.internal.tmf.chart.ui.dialog.Messages;
+import org.eclipse.tracecompass.internal.tmf.chart.ui.type.BarChartTypeDefinition;
+import org.eclipse.tracecompass.internal.tmf.chart.ui.type.IChartTypeDefinition;
+import org.eclipse.tracecompass.internal.tmf.chart.ui.type.ScatterChartTypeDefinition;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * This dialog is used for configuring series before making a chart.
+ *
+ * @author Gabriel-Andrew Pollo-Guilbert
+ */
+public class ChartMakerDialog extends Dialog {
+
+    private static final Image DELETE_IMAGE = NonNullUtils.checkNotNull(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE));
+
+    // ------------------------------------------------------------------------
+    // Members
+    // ------------------------------------------------------------------------
+
+    /**
+     * Data model used for the creation of the chart
+     */
+    private final IDataChartProvider<?> fDataProvider;
+    /**
+     * Table for displaying chart types
+     */
+    private final TableViewer fTypeTable;
+    /**
+     * Table for displaying series
+     */
+    private final TableViewer fSeriesTable;
+    /**
+     * Table for displaying valid X series
+     */
+    private final TableViewer fSelectionXTable;
+    /**
+     * Checkbox table for displaying valid Y series
+     */
+    private final CheckboxTableViewer fSelectionYTable;
+    /**
+     * Button used for creating a series
+     */
+    private final Button fAddButton;
+    /**
+     * Warning label that choices might be restricted after selection
+     */
+    private final Label fWarningLabel;
+    /**
+     * Checkbox for indicating whether X axis is logarithmic
+     */
+    private final Button fXLogscaleButton;
+    /**
+     * Checkbox for indicating whether Y axis is logarithmic
+     */
+    private final Button fYLogscaleButton;
+    /**
+     * List of created series
+     */
+    private final List<ChartSeries> fSelectedSeries = new ArrayList<>();
+    /**
+     * Parent composite
+     */
+    private Composite fComposite;
+    /**
+     * Currently selected chart type
+     */
+    private @Nullable IChartTypeDefinition fType;
+
+    /**
+     * Filter for X data descriptors
+     */
+    private @Nullable IDataChartDescriptor<?, ?> fXFilter;
+    /**
+     * Filter for Y data descriptors
+     */
+    private @Nullable IDataChartDescriptor<?, ?> fYFilter;
+    /**
+     * Chart data created after the dialog
+     */
+    private @Nullable ChartData fDataSeries;
+    /**
+     * Chart model created after the dialog
+     */
+    private @Nullable ChartModel fChartModel;
+
+    // ------------------------------------------------------------------------
+    // Important methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructor.
+     *
+     * @param parent
+     *            Parent shell
+     * @param model
+     *            Model to choose source from
+     */
+    public ChartMakerDialog(Shell parent, IDataChartProvider<?> model) {
+        super(parent);
+
+        fComposite = parent;
+        fDataProvider = model;
+
+        /* Create tables */
+        fTypeTable = new TableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER);
+        fSeriesTable = new TableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER);
+        fSelectionXTable = new TableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER | SWT.NO_SCROLL | SWT.V_SCROLL);
+        fSelectionYTable = checkNotNull(CheckboxTableViewer.newCheckList(parent, SWT.BORDER | SWT.NO_SCROLL | SWT.V_SCROLL));
+
+        /* Create buttons */
+        fAddButton = new Button(parent, SWT.NONE);
+        fXLogscaleButton = new Button(parent, SWT.CHECK);
+        fYLogscaleButton = new Button(parent, SWT.CHECK);
+        fWarningLabel = new Label(parent, SWT.NONE);
+
+        setShellStyle(getShellStyle() | SWT.RESIZE);
+    }
+
+    /**
+     * @return The configured data series
+     */
+    public @Nullable ChartData getDataSeries() {
+        return fDataSeries;
+    }
+
+    /**
+     * @return The configured chart model
+     */
+    public @Nullable ChartModel getChartModel() {
+        return fChartModel;
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    @Override
+    public Point getInitialSize() {
+        return new Point(800, 600);
+    }
+
+    @Override
+    public void create() {
+        super.create();
+
+        getButton(IDialogConstants.OK_ID).setEnabled(false);
+    }
+
+    @Override
+    public Composite createDialogArea(@Nullable Composite parent) {
+        fComposite = checkNotNull((Composite) super.createDialogArea(parent));
+        getShell().setText(Messages.ChartMakerDialog_Title);
+
+        /* Layouts */
+        GridLayout baseLayout = new GridLayout();
+        baseLayout.numColumns = 2;
+
+        GridData genericFillGridData = new GridData();
+        genericFillGridData.horizontalAlignment = SWT.FILL;
+        genericFillGridData.verticalAlignment = SWT.FILL;
+        genericFillGridData.grabExcessHorizontalSpace = true;
+        genericFillGridData.grabExcessVerticalSpace = true;
+
+        fComposite.setLayout(baseLayout);
+
+        /* Chart type selector */
+        createTypeTable();
+
+        /* Selected series viewer */
+        createSelectedSeriesGroup(genericFillGridData);
+
+        /* Series creator */
+        createSeriesCreatorGroup(genericFillGridData);
+
+        /* Options */
+        createOptionsGroup();
+
+        return fComposite;
+    }
+
+    // Create the type table, on the left side of the dialog
+    private void createTypeTable() {
+        GridData typeGridData = new GridData();
+        typeGridData.verticalSpan = 3;
+        typeGridData.horizontalAlignment = SWT.FILL;
+        typeGridData.verticalAlignment = SWT.FILL;
+        typeGridData.grabExcessHorizontalSpace = false;
+        typeGridData.grabExcessVerticalSpace = true;
+
+        TableViewerColumn typeColumn = new TableViewerColumn(fTypeTable, SWT.NONE);
+        typeColumn.getColumn().setWidth(50);
+        typeColumn.setLabelProvider(new TypeLabelProvider());
+
+        List<IChartTypeDefinition> types = new ArrayList<>();
+        types.add(new BarChartTypeDefinition());
+        types.add(new ScatterChartTypeDefinition());
+
+        fTypeTable.getTable().setParent(fComposite);
+        fTypeTable.getTable().setLayoutData(typeGridData);
+        fTypeTable.setContentProvider(ArrayContentProvider.getInstance());
+        fTypeTable.addSelectionChangedListener(new TypeSelectionListener());
+        fTypeTable.setInput(types);
+    }
+
+    // Create the group that shows the selected series
+    private void createSelectedSeriesGroup(GridData genericFillGridData) {
+        /**
+         * FIXME: The labels in the first column cannot be aligned to the
+         * center. The workaround is to put a dummy column that won't appear.
+         */
+        GridLayout genericGridLayout = new GridLayout();
+
+        TableViewerColumn dummyColumn = new TableViewerColumn(fSeriesTable, SWT.NONE);
+        dummyColumn.setLabelProvider(new SeriesDummyLabelProvider());
+
+        /* X series column */
+        TableViewerColumn xSelectionColumn = new TableViewerColumn(fSeriesTable, SWT.NONE);
+        xSelectionColumn.getColumn().setText(Messages.ChartMakerDialog_XSeries);
+        xSelectionColumn.getColumn().setAlignment(SWT.CENTER);
+        xSelectionColumn.getColumn().setResizable(false);
+        xSelectionColumn.setLabelProvider(new SeriesXLabelProvider());
+
+        /* Y series column */
+        TableViewerColumn ySelectionColumn = new TableViewerColumn(fSeriesTable, SWT.NONE);
+        ySelectionColumn.getColumn().setText(Messages.ChartMakerDialog_YSeries);
+        ySelectionColumn.getColumn().setAlignment(SWT.CENTER);
+        ySelectionColumn.getColumn().setResizable(false);
+        ySelectionColumn.setLabelProvider(new SeriesYLabelProvider());
+
+        /* Remove buttons column */
+        TableViewerColumn removeColumn = new TableViewerColumn(fSeriesTable, SWT.NONE);
+        removeColumn.getColumn().setResizable(false);
+        removeColumn.setLabelProvider(new SeriesRemoveLabelProvider());
+
+        TableColumnLayout seriesLayout = new TableColumnLayout();
+        seriesLayout.setColumnData(dummyColumn.getColumn(), new ColumnPixelData(0));
+        seriesLayout.setColumnData(xSelectionColumn.getColumn(), new ColumnWeightData(50));
+        seriesLayout.setColumnData(ySelectionColumn.getColumn(), new ColumnWeightData(50));
+        seriesLayout.setColumnData(removeColumn.getColumn(), new ColumnPixelData(34));
+
+        Group seriesGroup = new Group(fComposite, SWT.BORDER | SWT.FILL);
+        seriesGroup.setText(Messages.ChartMakerDialog_SelectedSeries);
+        seriesGroup.setLayout(genericGridLayout);
+        seriesGroup.setLayoutData(genericFillGridData);
+
+        Composite seriesComposite = new Composite(seriesGroup, SWT.NONE);
+        seriesComposite.setLayout(seriesLayout);
+        seriesComposite.setLayoutData(genericFillGridData);
+
+        fSeriesTable.getTable().setParent(seriesComposite);
+        fSeriesTable.getTable().setHeaderVisible(true);
+        fSeriesTable.getTable().addListener(SWT.MeasureItem, new SeriesRowResize());
+        fSeriesTable.setContentProvider(ArrayContentProvider.getInstance());
+        fSeriesTable.setInput(fSelectedSeries);
+    }
+
+    // Create the series selection group, listing all the available series
+    private void createSeriesCreatorGroup(GridData genericFillGridData) {
+        GridLayout creatorLayout = new GridLayout();
+        creatorLayout.numColumns = 2;
+
+        Group creatorGroup = new Group(fComposite, SWT.BORDER);
+        creatorGroup.setText(Messages.ChartMakerDialog_SeriesCreator);
+        creatorGroup.setLayout(creatorLayout);
+        creatorGroup.setLayoutData(genericFillGridData);
+
+        GridData creatorLabelGridData = new GridData();
+        creatorLabelGridData.horizontalAlignment = SWT.CENTER;
+        creatorLabelGridData.verticalAlignment = SWT.BOTTOM;
+
+        /* Top labels */
+        Label creatorLabelX = new Label(creatorGroup, SWT.NONE);
+        creatorLabelX.setText(Messages.ChartMakerDialog_XAxis);
+        creatorLabelX.setLayoutData(creatorLabelGridData);
+
+        Label creatorLabelY = new Label(creatorGroup, SWT.NONE);
+        creatorLabelY.setText(Messages.ChartMakerDialog_YAxis);
+        creatorLabelY.setLayoutData(creatorLabelGridData);
+
+        /* X axis table */
+        TableViewerColumn creatorXColumn = new TableViewerColumn(fSelectionXTable, SWT.NONE);
+        creatorXColumn.getColumn().setResizable(false);
+        creatorXColumn.setLabelProvider(new DataDescriptorLabelProvider());
+
+        TableColumnLayout creatorXLayout = new TableColumnLayout();
+        creatorXLayout.setColumnData(creatorXColumn.getColumn(), new ColumnWeightData(100));
+
+        Composite creatorXComposite = new Composite(creatorGroup, SWT.NONE);
+        creatorXComposite.setLayout(creatorXLayout);
+        creatorXComposite.setLayoutData(genericFillGridData);
+
+        fSelectionXTable.getTable().setParent(creatorXComposite);
+        fSelectionXTable.setContentProvider(ArrayContentProvider.getInstance());
+        fSelectionXTable.setInput(fDataProvider.getDataDescriptors());
+        fSelectionXTable.setFilters(new CreatorXFilter());
+        fSelectionXTable.addSelectionChangedListener(new CreatorXSelectedEvent());
+
+        /* Y axis table */
+        TableViewerColumn creatorYColumn = new TableViewerColumn(fSelectionYTable, SWT.NONE);
+        creatorYColumn.getColumn().setResizable(false);
+        creatorYColumn.setLabelProvider(new DataDescriptorLabelProvider());
+
+        TableColumnLayout creatorYLayout = new TableColumnLayout();
+        creatorYLayout.setColumnData(creatorYColumn.getColumn(), new ColumnWeightData(100));
+
+        Composite creatorYComposite = new Composite(creatorGroup, SWT.NONE);
+        creatorYComposite.setLayout(creatorYLayout);
+        creatorYComposite.setLayoutData(genericFillGridData);
+
+        fSelectionYTable.getTable().setParent(creatorYComposite);
+        fSelectionYTable.setContentProvider(ArrayContentProvider.getInstance());
+        fSelectionYTable.setInput(fDataProvider.getDataDescriptors());
+        fSelectionYTable.setFilters(new CreatorYFilter());
+        fSelectionYTable.addCheckStateListener(new CreatorYSelectedEvent());
+
+        /* Selected series warning */
+        fWarningLabel.setParent(creatorGroup);
+        fWarningLabel.setText(Messages.ChartMakerDialog_SelectionRestrictionWarning);
+        fWarningLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
+        fWarningLabel.setVisible(false);
+
+        /* Add button */
+        Label creatorLabelEmpty = new Label(creatorGroup, SWT.NONE);
+        creatorLabelEmpty.setText(""); //$NON-NLS-1$
+
+        GridData creatorButtonGridData = new GridData();
+        creatorButtonGridData.horizontalAlignment = SWT.RIGHT;
+        creatorButtonGridData.widthHint = 30;
+        creatorButtonGridData.heightHint = 30;
+
+        Image addImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD);
+        fAddButton.setParent(creatorGroup);
+        fAddButton.setLayoutData(creatorButtonGridData);
+        fAddButton.setImage(addImage);
+        fAddButton.addListener(SWT.Selection, new AddButtonClickedEvent());
+    }
+
+
+    private void createOptionsGroup() {
+        GridLayout optionsLayout = new GridLayout();
+        optionsLayout.numColumns = 2;
+
+        GridData configOptionsGridData = new GridData();
+        configOptionsGridData.horizontalAlignment = SWT.FILL;
+        configOptionsGridData.grabExcessHorizontalSpace = true;
+
+        Group optionsGroup = new Group(fComposite, SWT.BORDER);
+        optionsGroup.setText(Messages.ChartMakerDialog_Options);
+        optionsGroup.setLayout(optionsLayout);
+        optionsGroup.setLayoutData(configOptionsGridData);
+
+        /* Checkboxes for logscale */
+        fXLogscaleButton.setParent(optionsGroup);
+        fXLogscaleButton.setText(Messages.ChartMakerDialog_LogScaleX);
+
+        fYLogscaleButton.setParent(optionsGroup);
+        fYLogscaleButton.setText(Messages.ChartMakerDialog_LogScaleY);
+    }
+
+    @Override
+    public void okPressed() {
+        /* Create the data series */
+        fDataSeries = new ChartData(fDataProvider, fSelectedSeries);
+
+        /* Create the data model */
+        ChartType type = checkNotNull(checkNotNull(fType).getType());
+        String title = fDataProvider.getName();
+        boolean xlog = fXLogscaleButton.getSelection();
+        boolean ylog = fYLogscaleButton.getSelection();
+        fChartModel = new ChartModel(type, title, xlog, ylog);
+
+        super.okPressed();
+    }
+
+    // ------------------------------------------------------------------------
+    // Util methods
+    // ------------------------------------------------------------------------
+
+    private boolean checkIfSeriesCompatible(IChartTypeDefinition typeA, IChartTypeDefinition typeB) {
+        for (ChartSeries series : fSelectedSeries) {
+            if (typeA.checkIfXDescriptorValid(series.getX(), null) != typeB.checkIfXDescriptorValid(series.getX(), null)) {
+                return false;
+            }
+
+            if (typeA.checkIfYDescriptorValid(series.getY(), null) != typeB.checkIfYDescriptorValid(series.getY(), null)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean checkIfButtonReady() {
+        if (fSelectionXTable.getSelection().isEmpty()) {
+            return false;
+        }
+
+        if (fSelectionYTable.getCheckedElements().length == 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean checkIfSeriesPresent(ChartSeries test) {
+        for (ChartSeries series : fSelectedSeries) {
+            if (series.getX() == test.getX() && series.getY() == test.getY()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private @Nullable ChartSeries findRemoveButtonOwner(Button button) {
+        for (ChartSeries series : fSelectedSeries) {
+            ChartSeriesDialog line = (ChartSeriesDialog) series;
+            if (line.getButton() == button) {
+                return line;
+            }
+        }
+
+        return null;
+    }
+
+    private void removeIncompatibleSeries(IChartTypeDefinition type) {
+        Iterator<ChartSeries> iterator = fSelectedSeries.iterator();
+
+        while (iterator.hasNext()) {
+            ChartSeriesDialog series = (ChartSeriesDialog) iterator.next();
+
+            /* Check if the series is compatible, if not, remove it */
+            if (!type.checkIfXDescriptorValid(series.getX(), null) || !type.checkIfYDescriptorValid(series.getY(), null)) {
+                series.dispose();
+
+                /* Remove the series of the series list */
+                iterator.remove();
+            }
+        }
+    }
+
+    private void unselectIncompatibleSeries(IChartTypeDefinition type) {
+        /* Check if X selected series is compatible */
+        IDataChartDescriptor<?, ?> descriptorX = (IDataChartDescriptor<?, ?>) fSelectionXTable.getStructuredSelection().getFirstElement();
+        if (descriptorX != null && !type.checkIfXDescriptorValid(descriptorX, fXFilter)) {
+            fSelectionXTable.getTable().deselectAll();
+        }
+
+        /* Check if Y selected series are compatible */
+        for (Object element : fSelectionYTable.getCheckedElements()) {
+            IDataChartDescriptor<?, ?> descriptorY = (IDataChartDescriptor<?, ?>) checkNotNull(element);
+
+            if (!type.checkIfYDescriptorValid(descriptorY, fYFilter)) {
+                fSelectionYTable.setChecked(element, false);
+            }
+        }
+    }
+
+    private void configureLogscaleCheckboxes() {
+        /* Enable X logscale checkbox if possible */
+        if (checkNotNull(fType).checkIfXLogscalePossible(fXFilter)) {
+            fXLogscaleButton.setEnabled(true);
+        } else {
+            fXLogscaleButton.setEnabled(false);
+            fXLogscaleButton.setSelection(false);
+        }
+
+        /* Enable Y logscale checkbox if possible */
+        if (checkNotNull(fType).checkIfYLogscalePossible(fYFilter)) {
+            fYLogscaleButton.setEnabled(true);
+        } else {
+            fYLogscaleButton.setEnabled(false);
+            fYLogscaleButton.setSelection(false);
+        }
+    }
+
+    private boolean tryResetXFilter() {
+        if (fSelectedSeries.size() != 0) {
+            return false;
+        }
+
+        fXFilter = null;
+        return true;
+    }
+
+    private boolean tryResetYFilter() {
+        if (fSelectedSeries.size() != 0) {
+            return false;
+        }
+
+        if (fSelectionYTable.getCheckedElements().length != 0) {
+            return false;
+        }
+
+        fYFilter = null;
+        return true;
+    }
+
+    // ------------------------------------------------------------------------
+    // Listeners, Providers, etc
+    // ------------------------------------------------------------------------
+
+    /**
+     * This class extension avoid the use of an hashmap for linking a series
+     * with a button. Each button in the series table are linked to a series.
+     */
+    private class ChartSeriesDialog extends ChartSeries {
+        private final Button fButton;
+
+        private ChartSeriesDialog(IDataChartDescriptor<?, ?> descriptorX, IDataChartDescriptor<?, ?> descriptorY) {
+            super(descriptorX, descriptorY);
+            // Create the button for this series
+            Button button = new Button((Composite) fSeriesTable.getControl(), SWT.PUSH);
+            button.setImage(DELETE_IMAGE);
+            button.addListener(SWT.Selection, new SeriesRemoveButtonEvent());
+            fButton = button;
+        }
+
+        private Button getButton() {
+            return fButton;
+        }
+
+        public void dispose() {
+            fButton.dispose();
+        }
+
+    }
+
+    /**
+     * This provider provides the image in the chart type selection table.
+     */
+    private class TypeLabelProvider extends ColumnLabelProvider {
+        @Override
+        public @Nullable String getText(@Nullable Object element) {
+            return null;
+        }
+
+        @Override
+        public Image getImage(@Nullable Object element) {
+            IChartTypeDefinition type = checkNotNull((IChartTypeDefinition) element);
+            return new Image(fComposite.getDisplay(), type.getImageData());
+        }
+    }
+
+    /**
+     * This listener handle the selection in the chart type selection table.
+     */
+    private class TypeSelectionListener implements ISelectionChangedListener {
+        @Override
+        public void selectionChanged(@Nullable SelectionChangedEvent event) {
+            IStructuredSelection selection = fTypeTable.getStructuredSelection();
+            IChartTypeDefinition type = (IChartTypeDefinition) selection.getFirstElement();
+
+            if (type == null) {
+                return;
+            }
+
+            /* Check if the series are compatible with the chart type */
+            if (fSelectedSeries.size() != 0 && !checkIfSeriesCompatible(checkNotNull(fType), type)) {
+                String warning = Messages.ChartMakerDialog_WarningConfirm;
+                String message = String.format(Messages.ChartMakerDialog_WarningIncompatibleSeries,
+                        type.getType().toString().toLowerCase());
+
+                /* Ask the user if he wants to continue */
+                boolean choice = MessageDialog.openConfirm(fComposite.getShell(), warning, message);
+                if (!choice) {
+                    fTypeTable.setSelection(new StructuredSelection(fType));
+                    return;
+                }
+
+                removeIncompatibleSeries(type);
+                fSeriesTable.refresh();
+            }
+
+            fType = type;
+
+            /* Refresh controls */
+            unselectIncompatibleSeries(fType);
+
+            if (tryResetXFilter()) {
+                fSelectionXTable.refresh();
+            }
+
+            if (tryResetYFilter()) {
+                fSelectionYTable.refresh();
+            }
+
+            fAddButton.setEnabled(checkIfButtonReady());
+            configureLogscaleCheckboxes();
+        }
+    }
+
+    /**
+     * This dummy provider is used as a workaround in a column's bug.
+     */
+    private class SeriesDummyLabelProvider extends ColumnLabelProvider {
+        @Override
+        public @Nullable String getText(@Nullable Object element) {
+            return null;
+        }
+    }
+
+    /**
+     * This provider provides the labels for the X column of the series table.
+     */
+    private class SeriesXLabelProvider extends ColumnLabelProvider {
+        @Override
+        public String getText(@Nullable Object element) {
+            ChartSeries series = checkNotNull((ChartSeries) element);
+            return series.getX().getLabel();
+        }
+    }
+
+    /**
+     * This provider provides the labels for the Y column of the series table.
+     */
+    private class SeriesYLabelProvider extends ColumnLabelProvider {
+        @Override
+        public String getText(@Nullable Object element) {
+            ChartSeries series = checkNotNull((ChartSeries) element);
+            return series.getY().getLabel();
+        }
+    }
+
+    /**
+     * This provider provides the buttons for removing a series in the series
+     * table.
+     */
+    private class SeriesRemoveLabelProvider extends ColumnLabelProvider {
+        @Override
+        public @Nullable String getText(@Nullable Object element) {
+            return null;
+        }
+
+        @Override
+        public void update(@Nullable ViewerCell cell) {
+            if (cell == null) {
+                return;
+            }
+
+            /* Create a button if it doesn't exist */
+            ChartSeriesDialog series = (ChartSeriesDialog) cell.getViewerRow().getElement();
+            Button button = series.getButton();
+
+            /* Set the position of the button into the cell */
+            TableItem item = (TableItem) cell.getItem();
+            TableEditor editor = new TableEditor(item.getParent());
+            editor.grabHorizontal = true;
+            editor.grabVertical = true;
+            editor.setEditor(button, item, cell.getColumnIndex());
+            editor.layout();
+        }
+    }
+
+    /**
+     * This listener handles the event when the button for removing a series is
+     * clicked.
+     */
+    private class SeriesRemoveButtonEvent implements Listener {
+        @Override
+        public void handleEvent(@Nullable Event event) {
+            if (event == null) {
+                return;
+            }
+
+            /* Dispose the button of the series */
+            Button button = (Button) checkNotNull(event.widget);
+            button.dispose();
+
+            /* Remove the series from the list */
+            ChartSeries series = findRemoveButtonOwner(button);
+            fSelectedSeries.remove(series);
+            fSeriesTable.refresh();
+
+            /* Refresh controls */
+            tryResetXFilter();
+            fSelectionXTable.refresh();
+
+            tryResetYFilter();
+            fSelectionYTable.refresh();
+
+            /* Disable OK button if no series are made */
+            if (fSelectedSeries.size() == 0) {
+                getButton(IDialogConstants.OK_ID).setEnabled(false);
+                fWarningLabel.setVisible(false);
+            }
+
+            configureLogscaleCheckboxes();
+        }
+    }
+
+    /**
+     * This listener resizes the height of each row of the series table.
+     */
+    private class SeriesRowResize implements Listener {
+        @Override
+        public void handleEvent(@Nullable Event event) {
+            if (event == null) {
+                return;
+            }
+
+            event.height = 27;
+        }
+    }
+
+    /**
+     * This provider provides labels for {@link DataChartDescriptor}.
+     */
+    private class DataDescriptorLabelProvider extends ColumnLabelProvider {
+        @Override
+        public String getText(@Nullable Object element) {
+            IDataChartDescriptor<?, ?> descriptor = (IDataChartDescriptor<?, ?>) checkNotNull(element);
+            return descriptor.getLabel();
+        }
+    }
+
+    /**
+     * This filter is used for filtering labels in the X selection table.
+     */
+    private class CreatorXFilter extends ViewerFilter {
+        @Override
+        public boolean select(@Nullable Viewer viewer, @Nullable Object parentElement, @Nullable Object element) {
+            IChartTypeDefinition type = fType;
+
+            if (type == null) {
+                return false;
+            }
+
+            IDataChartDescriptor<?, ?> descriptor = (IDataChartDescriptor<?, ?>) checkNotNull(element);
+            return type.checkIfXDescriptorValid(descriptor, fXFilter);
+        }
+    }
+
+    /**
+     * This filter is used for filtering labels in the Y selection table.
+     */
+    private class CreatorYFilter extends ViewerFilter {
+        @Override
+        public boolean select(@Nullable Viewer viewer, @Nullable Object parentElement, @Nullable Object element) {
+            IChartTypeDefinition type = fType;
+
+            if (type == null) {
+                return false;
+            }
+
+            IDataChartDescriptor<?, ?> descriptor = (IDataChartDescriptor<?, ?>) checkNotNull(element);
+            return type.checkIfYDescriptorValid(descriptor, fYFilter);
+        }
+    }
+
+    /**
+     * This listener handles the event when a selection is made in the X
+     * selection table.
+     */
+    private class CreatorXSelectedEvent implements ISelectionChangedListener {
+        @Override
+        public void selectionChanged(@Nullable SelectionChangedEvent event) {
+            /* Enable button if possible */
+            fAddButton.setEnabled(checkIfButtonReady());
+        }
+    }
+
+    /**
+     * This listener handles the event when a value is checked in the Y
+     * selection table.
+     */
+    private class CreatorYSelectedEvent implements ICheckStateListener {
+        @Override
+        public void checkStateChanged(@Nullable CheckStateChangedEvent event) {
+            if (event == null) {
+                return;
+            }
+
+            /* Set Y filter if needed */
+            if (event.getChecked()) {
+                if (fYFilter == null) {
+                    IDataChartDescriptor<?, ?> descriptor = (IDataChartDescriptor<?, ?>) event.getElement();
+                    fYFilter = descriptor;
+                }
+            } else {
+                tryResetYFilter();
+            }
+
+            /* Refresh controls */
+            fSelectionYTable.refresh();
+            fAddButton.setEnabled(checkIfButtonReady());
+
+            configureLogscaleCheckboxes();
+        }
+    }
+
+    /**
+     * This listener handle the event when the add button of the series creator
+     * is clicked.
+     */
+    private class AddButtonClickedEvent implements Listener {
+        @Override
+        public void handleEvent(@Nullable Event event) {
+            IDataChartDescriptor<?, ?> descriptorX = (IDataChartDescriptor<?, ?>) checkNotNull(fSelectionXTable.getStructuredSelection().getFirstElement());
+            Object[] descriptorsY = fSelectionYTable.getCheckedElements();
+
+            /* Create a series for each Y axis */
+            for (int i = 0; i < descriptorsY.length; i++) {
+                IDataChartDescriptor<?, ?> descriptorY = (IDataChartDescriptor<?, ?>) descriptorsY[i];
+                ChartSeriesDialog series = new ChartSeriesDialog(descriptorX, checkNotNull(descriptorY));
+
+                if (!checkIfSeriesPresent(series)) {
+                    fSelectedSeries.add(series);
+                }
+            }
+
+            /* Set the X filter */
+            if (fXFilter == null) {
+                fXFilter = descriptorX;
+            }
+
+            /* Refresh controls */
+            // FIXME: The first refresh creates the buttons, the second makes
+            // sure they show correctly, otherwise one button is displayed only
+            // half his size until another call to refresh magically makes them
+            // all show correctly
+            fSeriesTable.refresh();
+            Display.getDefault().asyncExec(() -> fSeriesTable.refresh());
+            fSelectionXTable.refresh();
+
+            /* Enable OK button */
+            getButton(IDialogConstants.OK_ID).setEnabled(true);
+            fWarningLabel.setVisible(true);
+
+            configureLogscaleCheckboxes();
+        }
+    }
+
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/provisional/tmf/chart/ui/dialog/package-info.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/provisional/tmf/chart/ui/dialog/package-info.java
new file mode 100644 (file)
index 0000000..97c7a40
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.dialog;
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/dialog/Messages.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/dialog/Messages.java
new file mode 100644 (file)
index 0000000..27255fb
--- /dev/null
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.chart.ui.dialog;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for the chart package
+ *
+ * @author Gabriel-Andrew Pollo-Guilbert
+ */
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+    /** Title for the selection of data */
+    public static @Nullable String ChartMakerDialog_AvailableData;
+    /** Title for the selection of the chart type */
+    public static @Nullable String ChartMakerDialog_ChartType;
+    /** Title for the enabling logarithmic scale for the X axis */
+    public static @Nullable String ChartMakerDialog_LogScaleX;
+    /** Title for the enabling logarithmic scale for the Y axis */
+    public static @Nullable String ChartMakerDialog_LogScaleY;
+    /** Title for the options group */
+    public static @Nullable String ChartMakerDialog_Options;
+    /** Title for the series creator group */
+    public static @Nullable String ChartMakerDialog_SeriesCreator;
+    /** Title for choosing the data source for the X axis */
+    public static @Nullable String ChartMakerDialog_XAxis;
+    /** Title for choosing the data source for the Y axis */
+    public static @Nullable String ChartMakerDialog_YAxis;
+    /** Warning label that choices might be restricted when there is a selection */
+    public static @Nullable String ChartMakerDialog_SelectionRestrictionWarning;
+    /** Title for the series group */
+    public static @Nullable String ChartMakerDialog_SelectedSeries;
+    /** Title of the chark maker dialog */
+    public static @Nullable String ChartMakerDialog_Title;
+    /** Title for X series */
+    public static @Nullable String ChartMakerDialog_XSeries;
+    /** Title for Y series */
+    public static @Nullable String ChartMakerDialog_YSeries;
+    /** Title of a warning dialog */
+    public static @Nullable String ChartMakerDialog_WarningConfirm;
+    /** Message of the warning dialog when there is incompatible series */
+    public static @Nullable String ChartMakerDialog_WarningIncompatibleSeries;
+
+    /** Message of the warning dialog when there is incompatible series */
+    public static @Nullable String ChartSeries_MultiSeriesTitle;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/dialog/messages.properties b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/dialog/messages.properties
new file mode 100644 (file)
index 0000000..834c801
--- /dev/null
@@ -0,0 +1,26 @@
+###############################################################################
+# Copyright (c) 2016 École Polytechnique de Montréal
+#
+# All rights reserved. This program and the accompanying materials are
+# made available under the terms of the Eclipse Public License v1.0 which
+# accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+ChartMakerDialog_AvailableData=Available Data
+ChartMakerDialog_ChartType=Chart Type
+ChartMakerDialog_LogScaleX=Logarithmic Scale X
+ChartMakerDialog_LogScaleY=Logarithmic Scale Y
+ChartMakerDialog_Options=Options
+ChartMakerDialog_SeriesCreator=Series Creator
+ChartMakerDialog_XAxis=X Axis
+ChartMakerDialog_YAxis=Y Axis
+ChartMakerDialog_SelectionRestrictionWarning=Note: Selection might be restricted based on type\nchecking of previously selected series
+ChartMakerDialog_SelectedSeries=Selected Series
+ChartMakerDialog_Title=Custom chart creation
+ChartMakerDialog_XSeries=X Series
+ChartMakerDialog_YSeries=Y Series
+ChartMakerDialog_WarningConfirm=Confirm
+ChartMakerDialog_WarningIncompatibleSeries=At least one selected series is not compatible with a %s. Do you want to continue?
+
+ChartSeries_MultiSeriesTitle={0} by {1}
\ No newline at end of file
index e2a54ad873feb56cf5bf57c4160a0504ffa75f4b..9da7b97f658d7c5faac358317bd26aaef22f962d 100644 (file)
@@ -22,6 +22,7 @@ import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseMoveListener;
@@ -51,6 +52,7 @@ import org.eclipse.tracecompass.internal.tmf.chart.ui.consumer.ScatterStringCons
 import org.eclipse.tracecompass.internal.tmf.chart.ui.consumer.XYChartConsumer;
 import org.eclipse.tracecompass.internal.tmf.chart.ui.consumer.XYSeriesConsumer;
 import org.eclipse.tracecompass.internal.tmf.chart.ui.data.ChartRangeMap;
+import org.eclipse.tracecompass.internal.tmf.chart.ui.dialog.Messages;
 import org.eclipse.tracecompass.internal.tmf.chart.ui.format.LabelFormat;
 import org.swtchart.Chart;
 import org.swtchart.IAxis;
@@ -235,6 +237,11 @@ public final class SwtScatterChart extends SwtXYChartViewer {
     protected ISeries createSwtSeries(ChartSeries chartSeries, ISeriesSet swtSeriesSet, @NonNull Color color) {
         String title = chartSeries.getY().getName();
 
+        boolean multiSeries = (getXDescriptors().stream().distinct().count() > 1);
+        if (multiSeries) {
+            title = NLS.bind(Messages.ChartSeries_MultiSeriesTitle, title, chartSeries.getX().getLabel());
+        }
+
         ILineSeries swtSeries = (ILineSeries) swtSeriesSet.createSeries(SeriesType.LINE, title);
         swtSeries.setLineStyle(LineStyle.NONE);
         swtSeries.setSymbolColor(color);
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/BarChartTypeDefinition.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/BarChartTypeDefinition.java
new file mode 100644 (file)
index 0000000..71ffd3e
--- /dev/null
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.chart.ui.type;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartType;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.DescriptorTypeVisitor;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.IDataChartDescriptor;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.DescriptorTypeVisitor.DescriptorType;
+
+/**
+ * Bar chart implementation of the a chart type.
+ *
+ * @author Gabriel-Andrew Pollo-Guilbert
+ */
+public class BarChartTypeDefinition implements IChartTypeDefinition {
+
+    // ------------------------------------------------------------------------
+    // Constants
+    // ------------------------------------------------------------------------
+
+    /**
+     * Icons used in the chart maker
+     */
+    private static final String BAR_CHART_ICON = "icons/barchart.png"; //$NON-NLS-1$
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    @Override
+    public ChartType getType() {
+        return ChartType.BAR_CHART;
+    }
+
+    @Override
+    public ImageData getImageData() {
+        return new ImageData(getClass().getClassLoader().getResourceAsStream(BAR_CHART_ICON));
+    }
+
+    @Override
+    public boolean checkIfXDescriptorValid(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter) {
+        DescriptorTypeVisitor visitor = new DescriptorTypeVisitor();
+        desc.accept(visitor);
+
+        // A bar chart only accepts string values for the X axis
+        if (!visitor.isIndividualType(DescriptorType.STRING)) {
+            return false;
+        }
+
+        // Only one descriptor is accepted for the X axis, so return true only
+        // if the descriptor is the filter.
+        if (filter != null) {
+            return desc.getName().equals(filter.getName());
+        }
+        return true;
+    }
+
+    @Override
+    public boolean checkIfYDescriptorValid(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter) {
+        DescriptorTypeVisitor visitor = new DescriptorTypeVisitor();
+        desc.accept(visitor);
+
+        // Only numerical data are accepted for the bar chart: it weighs the
+        // values and it is not possible to weigh Strings
+        if (visitor.isIndividualType(DescriptorType.STRING)) {
+            return false;
+        }
+
+        // Only allow descriptors of the same type, that will mean the same
+        // thing. It is hard to compare durations to timestamps for instance
+        return IChartTypeDefinition.filterSameDescriptor(desc, filter);
+    }
+
+    @Override
+    public boolean checkIfXLogscalePossible(@Nullable IDataChartDescriptor<?, ?> filter) {
+        // Only strings are allowed for X axis, so no log scale possible
+        return false;
+    }
+
+    @Override
+    public boolean checkIfYLogscalePossible(@Nullable IDataChartDescriptor<?, ?> filter) {
+        if (filter == null) {
+            return true;
+        }
+
+        // It would not make sense to allow log scales for timestamps, so it
+        // only applies to other numerical values
+        return IChartTypeDefinition.checkIfNumerical(filter) && !IChartTypeDefinition.checkIfTimestamp(filter);
+    }
+
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/IChartTypeDefinition.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/IChartTypeDefinition.java
new file mode 100644 (file)
index 0000000..499f207
--- /dev/null
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.chart.ui.type;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartType;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.DescriptorTypeVisitor;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.IDataChartDescriptor;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.DescriptorTypeVisitor.DescriptorType;
+
+/**
+ * Interface for implementing chart type specific carasteristics.
+ *
+ * @author Gabriel-Andrew Pollo-Guilbert
+ */
+public interface IChartTypeDefinition {
+
+    /**
+     * Accessor that returns the identifier of the chart.
+     *
+     * @return Identifier of the chart
+     */
+    ChartType getType();
+
+    /**
+     * Accessor that returns image data of the chart's icon.
+     *
+     * @return Image data of an icon
+     */
+    ImageData getImageData();
+
+    /**
+     * Method used for checking if a descriptor for the X axis is valid given
+     * previously selected descriptors.
+     *
+     * For example, if for one chart type, only string descriptors should be
+     * accepted, then this method will return true only for descriptors
+     * resolving to String values, independent on the filter.
+     *
+     * Also, if a chart type does not mix string and numerical values for the X
+     * axis, this method would always return <code>true</code> if the filter is
+     * <code>null</code>, but would check the type of the value the descriptor
+     * resolves to and return </code>true</code> only if the type matches.
+     *
+     * @param desc
+     *            The descriptor to validate
+     * @param filter
+     *            The descriptor used for filtering
+     * @return {@code true} if the descriptor passes the filter or {@code false}
+     *         if the descriptor didn't pass the filter
+     */
+    boolean checkIfXDescriptorValid(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter);
+
+    /**
+     * Method used for checking if a descriptor for the Y axis is valid given
+     * previously selected descriptors.
+     *
+     * For example, if for one chart type, only string descriptors should be
+     * accepted, then this method will return true only for descriptors
+     * resolving to String values, independent on the filter.
+     *
+     * Also, if a chart type does not mix string and numerical values for the Y
+     * axis, this method would always return <code>true</code> if the filter is
+     * <code>null</code>, but would check the type of the value the descriptor
+     * resolves to and return </code>true</code> only if the type matches.
+     *
+     * @param desc
+     *            The descriptor to check
+     * @param filter
+     *            The descriptor used for filtering
+     * @return {@code true} if the descriptor passes the filter or {@code false}
+     *         if the descriptor didn't pass the filter
+     */
+    boolean checkIfYDescriptorValid(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter);
+
+    /**
+     * Method that checks if the X axis logarithmic scale can be enabled.
+     *
+     * @param filter
+     *            An descriptor that represent the X series
+     * @return {@code true} if it can be logarithmic, {@code false} if can't be
+     */
+    boolean checkIfXLogscalePossible(@Nullable IDataChartDescriptor<?, ?> filter);
+
+    /**
+     * Method that checks if the Y axis logarithmic scale can be enabled.
+     *
+     * @param filter
+     *            An descriptor that represent the Y series
+     * @return {@code true} if it can be logarithmic, {@code false} if can't be
+     */
+    boolean checkIfYLogscalePossible(@Nullable IDataChartDescriptor<?, ?> filter);
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Utility method to check if a descriptor has the same class as the filter.
+     * It compares the class of the descriptor, not if it is an instanceof the
+     * same class, so this will return <code>true</code> only if the class is
+     * the same, not a child of.
+     *
+     * @param desc
+     *            The descriptor to check
+     * @param filter
+     *            The descriptor used for filtering
+     * @return {@code true} if both descriptors share the same class or the
+     *         filter is {@code null}, or {@code false} if they are different
+     */
+    static boolean filterSameDescriptor(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter) {
+        if (filter != null) {
+            if (desc.getClass() != filter.getClass()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Utility method to check if a descriptor is numerical. Durations and
+     * timestamps are considered numerical.
+     *
+     * @param desc
+     *            The descriptor to check
+     * @return {@code true} if the descriptor is numerical, {@code false} if the
+     *         descriptor is something else
+     */
+    static boolean checkIfNumerical(IDataChartDescriptor<?, ?> desc) {
+        DescriptorTypeVisitor visitor = new DescriptorTypeVisitor();
+        desc.accept(visitor);
+
+        return visitor.isIndividualType(DescriptorType.NUMERICAL);
+    }
+
+    /**
+     * Utility method to check if a descriptor is a timestamp
+     *
+     * @param desc
+     *            The descriptor to check
+     * @return {@code true} if the descriptor is a timestamp, {@code false} if
+     *         the descriptor is something else
+     */
+    static boolean checkIfTimestamp(IDataChartDescriptor<?, ?> desc) {
+        DescriptorTypeVisitor visitor = new DescriptorTypeVisitor();
+        desc.accept(visitor);
+
+        return visitor.isIndividualType(DescriptorType.TIMESTAMP);
+    }
+
+}
\ No newline at end of file
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/ScatterChartTypeDefinition.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/ScatterChartTypeDefinition.java
new file mode 100644 (file)
index 0000000..c4116b2
--- /dev/null
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.chart.ui.type;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartType;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.descriptor.IDataChartDescriptor;
+
+/**
+ * Scatter chart implementation of the a chart type.
+ *
+ * @author Gabriel-Andrew Pollo-Guilbert
+ */
+public class ScatterChartTypeDefinition implements IChartTypeDefinition {
+
+    // ------------------------------------------------------------------------
+    // Constants
+    // ------------------------------------------------------------------------
+
+    /**
+     * Icons used in the chart maker
+     */
+    private static final String SCATTER_CHART_ICON = "icons/scatterchart.png"; //$NON-NLS-1$
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    @Override
+    public ChartType getType() {
+        return ChartType.SCATTER_CHART;
+    }
+
+    @Override
+    public ImageData getImageData() {
+        return new ImageData(getClass().getClassLoader().getResourceAsStream(SCATTER_CHART_ICON));
+    }
+
+    @Override
+    public boolean checkIfXDescriptorValid(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter) {
+        // Only descriptors with the same type are accepted on the X axis
+        return IChartTypeDefinition.filterSameDescriptor(desc, filter);
+    }
+
+    @Override
+    public boolean checkIfYDescriptorValid(IDataChartDescriptor<?, ?> desc, @Nullable IDataChartDescriptor<?, ?> filter) {
+        // Only descriptors with the same type are accepted on the Y axis
+        return IChartTypeDefinition.filterSameDescriptor(desc, filter);
+    }
+
+    @Override
+    public boolean checkIfXLogscalePossible(@Nullable IDataChartDescriptor<?, ?> filter) {
+        // Enable logarithmic scale for numerical descriptors, but not
+        // timestamp, as log scale with timestamp would not make sense
+        return checkNumericalNotTimestamp(filter);
+    }
+
+    @Override
+    public boolean checkIfYLogscalePossible(@Nullable IDataChartDescriptor<?, ?> filter) {
+        // Enable logarithmic scale for numerical descriptors, but not
+        // timestamp, as log scale with timestamp would not make sense
+        return checkNumericalNotTimestamp(filter);
+    }
+
+    private static boolean checkNumericalNotTimestamp(@Nullable IDataChartDescriptor<?, ?> filter) {
+        if (filter == null) {
+            return true;
+        }
+
+        return IChartTypeDefinition.checkIfNumerical(filter) && !IChartTypeDefinition.checkIfTimestamp(filter);
+    }
+
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/package-info.java b/tmf/org.eclipse.tracecompass.tmf.chart.ui/src/org/eclipse/tracecompass/internal/tmf/chart/ui/type/package-info.java
new file mode 100644 (file)
index 0000000..6433994
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.tmf.chart.ui.type;
This page took 0.040061 seconds and 5 git commands to generate.