lami.ui: Replace LAMI charts with custom charts
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.lami.ui / src / org / eclipse / tracecompass / internal / provisional / analysis / lami / ui / views / LamiReportViewTabPage.java
index e9e8781ada7abe97f93e127b5df369f1d912c70d..f75a85078d5bc657e8a4640ca9268e040b78d0d2 100644 (file)
@@ -14,6 +14,7 @@ import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyStrin
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -28,15 +29,21 @@ import org.eclipse.jface.viewers.IStructuredContentProvider;
 import org.eclipse.jface.viewers.LabelProvider;
 import org.eclipse.jface.window.Window;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiEmptyAspect;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
+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.signal.ChartSelectionUpdateSignal;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.chart.IChartViewer;
+import org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.dialog.ChartMakerDialog;
 import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
@@ -55,6 +62,7 @@ import com.google.common.collect.Iterables;
  *
  * @author Alexandre Montplaisir
  * @author Jonathan Rajotte-Julien
+ * @author Gabriel-Andrew Pollo-Guilbert
  */
 public final class LamiReportViewTabPage extends TmfComponent {
 
@@ -63,13 +71,15 @@ public final class LamiReportViewTabPage extends TmfComponent {
     // ------------------------------------------------------------------------
 
     private final LamiResultTable fResultTable;
-
     private final LamiViewerControl fTableViewerControl;
     private final Set<LamiViewerControl> fCustomGraphViewerControls = new LinkedHashSet<>();
     private final Composite fControl;
 
     private Set<Integer> fSelectionIndexes;
 
+    private @Nullable IChartViewer fChart;
+    private Set<Object> fSelection;
+
     // ------------------------------------------------------------------------
     // Constructor
     // ------------------------------------------------------------------------
@@ -84,25 +94,31 @@ public final class LamiReportViewTabPage extends TmfComponent {
      */
     public LamiReportViewTabPage(Composite parent, LamiResultTable table) {
         super(table.getTableClass().getTableTitle());
+
         fResultTable = table;
         fSelectionIndexes = new HashSet<>();
         fSelectionIndexes = getIndexOfEntriesIntersectingTimerange(checkNotNull(fResultTable), TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange());
 
         fControl = parent;
 
+        /* Map the current trace selection to our lami entry */
+        fSelection = getEntriesIntersectingTimerange(fResultTable, TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange());
+
         /* Prepare the table viewer, which is always present */
         LamiViewerControl tableViewerControl = new LamiViewerControl(fControl, this);
         fTableViewerControl = tableViewerControl;
 
         /* Automatically open the table viewer initially */
-        tableViewerControl.getToggleAction().run();
+        fTableViewerControl.getToggleAction().run();
 
         /* Simulate a new external signal to the default viewer */
         LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, fSelectionIndexes, this);
         TmfSignalManager.dispatchSignal(signal);
+        ChartSelectionUpdateSignal chartSignal = new ChartSelectionUpdateSignal(this, fResultTable, fSelection);
+        TmfSignalManager.dispatchSignal(chartSignal);
 
+        /* Dispose this class's resource */
         fControl.addDisposeListener(e -> {
-            /* Dispose this class's resource */
             fTableViewerControl.dispose();
             clearAllCustomViewers();
             super.dispose();
@@ -110,31 +126,49 @@ public final class LamiReportViewTabPage extends TmfComponent {
     }
 
     // ------------------------------------------------------------------------
-    // Operations
+    // Overriden methods
     // ------------------------------------------------------------------------
 
     @Override
     public void dispose() {
+        /* fControl's listner will dispose other resources */
         fControl.dispose();
-        /* fControl's disposeListener will dispose the class's resources */
     }
 
-    /**
-     * Get the SWT control associated with this tab page.
-     *
-     * @return The SWT control
-     */
-    public Composite getControl() {
-        return fControl;
-    }
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
 
     /**
-     * Get the result table shown in this tab.
-     *
-     * @return The report result table
+     * This method is used for creating a chart from the result table of the
+     * analyse. It uses the custom charts plugin to configure and create the
+     * chart.
      */
-    public LamiResultTable getResultTable() {
-        return fResultTable;
+    public void createNewCustomChart() {
+        Shell shell = this.getControl().getShell();
+        if (shell == null) {
+            return;
+        }
+
+        /* Open the chart maker dialog */
+        ChartMakerDialog dialog = new ChartMakerDialog(shell, fResultTable);
+        if (dialog.open() != Window.OK) {
+            return;
+        }
+
+        /* Make sure the data for making a chart was generated */
+        ChartData data = dialog.getDataSeries();
+        ChartModel model = dialog.getChartModel();
+        if (data == null || model == null) {
+            return;
+        }
+
+        /* Make a chart with the factory constructor */
+        fChart = IChartViewer.createChart(fControl, data, model);
+        /* Signal the current selection to the newly created graph */
+        ChartSelectionUpdateSignal signal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this,
+                fResultTable, fSelection);
+        TmfSignalManager.dispatchSignal(signal);
     }
 
     /**
@@ -153,6 +187,28 @@ public final class LamiReportViewTabPage extends TmfComponent {
         fTableViewerControl.getToggleAction().run();
     }
 
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the SWT control associated with this tab page.
+     *
+     * @return The SWT control
+     */
+    public Composite getControl() {
+        return fControl;
+    }
+
+    /**
+     * Get the result table shown in this tab.
+     *
+     * @return The report result table
+     */
+    public LamiResultTable getResultTable() {
+        return fResultTable;
+    }
+
     /**
      * Add a new chart viewer to this tab.
      *
@@ -163,7 +219,7 @@ public final class LamiReportViewTabPage extends TmfComponent {
      * @param chartType
      *            The type of chart to create
      */
-    public void createNewCustomChart(ChartType chartType) {
+    public void createNewCustomChart(LamiChartType chartType) {
         int xLogScaleOptionIndex = -1;
         int yLogScaleOptionIndex = -1;
 
@@ -326,6 +382,7 @@ public final class LamiReportViewTabPage extends TmfComponent {
     // Signals
     // ------------------------------------------------------------------------
 
+    // Lami signals
     /**
      * Signal handler for selection update.
      * Propagate a TmfSelectionRangeUpdatedSignal if possible.
@@ -363,6 +420,8 @@ public final class LamiReportViewTabPage extends TmfComponent {
              * there is no notion of "unselected state" in most of the viewers so
              * we do not update/clear the last timerange and show false information to the user.
              */
+            ChartSelectionUpdateSignal customSignal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this, fResultTable, Collections.EMPTY_SET);
+            TmfSignalManager.dispatchSignal(customSignal);
             return;
         }
 
@@ -384,29 +443,20 @@ public final class LamiReportViewTabPage extends TmfComponent {
         }
 
         fSelectionIndexes = entryIndex;
-    }
-
-    /**
-     * Signal handler for time range selections
-     *
-     * @param signal
-     *            The received signal
-     */
-    @TmfSignalHandler
-    public void externalUpdateSelection(TmfSelectionRangeUpdatedSignal signal) {
-        LamiResultTable table = fResultTable;
 
-        if (signal.getSource() == this) {
-            /* We are the source */
-            return;
+        // Create the signal for the custom chart
+        List<LamiTableEntry> entries = fResultTable.getEntries();
+        Set<Object> selectionSet = new HashSet<>();
+        for (Integer selectionIndex : entryIndex) {
+            selectionSet.add(entries.get(selectionIndex));
         }
-        TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime());
+        fSelection = selectionSet;
+        ChartSelectionUpdateSignal customSignal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this, fResultTable, selectionSet);
+        TmfSignalManager.dispatchSignal(customSignal);
 
-        Set<Integer> selections = getIndexOfEntriesIntersectingTimerange(table, range);
-
-        /* Update all LamiViewer */
-        LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selections, this);
-        TmfSignalManager.dispatchSignal(signal1);
+//        /* Update all LamiViewer */
+//        LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selections, this);
+//        TmfSignalManager.dispatchSignal(signal1);
     }
 
     private static Set<Integer> getIndexOfEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) {
@@ -434,4 +484,174 @@ public final class LamiReportViewTabPage extends TmfComponent {
         }
         return selections;
     }
+
+    // Custom chart signals
+    /**
+     * Signal handler for a chart selection update. It will try to propagate a
+     * {@link TmfSelectionRangeUpdatedSignal} if possible.
+     *
+     * @param signal
+     *            The selection update signal
+     */
+    @TmfSignalHandler
+    public void updateSelection(ChartSelectionUpdateSignal signal) {
+        IChartViewer chart = fChart;
+        if (chart == null) {
+            return;
+        }
+
+        /* Make sure we are not sending a signal to ourself */
+        if (signal.getSource() == this) {
+            return;
+        }
+
+        /* Make sure the signal comes from the data provider's scope */
+        if (fResultTable.hashCode() != signal.getDataProvider().hashCode()) {
+            return;
+        }
+
+        /* Find which index row has been selected */
+        Set<Object> entries = signal.getSelectedObject();
+
+        /*
+         * Since most of the external viewer deal only with continuous timerange
+         * and do not allow multi time range selection simply signal only when
+         * only one selection is present.
+         */
+        if (entries.isEmpty()) {
+            /*
+             * In an ideal world we would send a null signal to reset all view
+             * and simply show no selection. But since this is Tracecompass
+             * there is no notion of "unselected state" in most of the viewers
+             * so we do not update/clear the last timerange and show false
+             * information to the user.
+             */
+            /* Signal all Lami viewers & views of the selection */
+            LamiSelectionUpdateSignal lamiSignal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, Collections.EMPTY_SET, this);
+            TmfSignalManager.dispatchSignal(lamiSignal);
+            return;
+        }
+
+        /* Update the selection */
+        fSelection = entries;
+
+        // Create the signal for the LAMI selection
+        Set<Integer> selectionIndexes = new HashSet<>();
+        for (Object entry : entries ) {
+            selectionIndexes.add(fResultTable.getEntries().indexOf(entry));
+        }
+
+        fSelectionIndexes = selectionIndexes;
+
+        /* Signal all Lami viewers & views of the selection */
+        LamiSelectionUpdateSignal lamiSignal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selectionIndexes, this);
+        TmfSignalManager.dispatchSignal(lamiSignal);
+
+        /* Only propagate to all TraceCompass if there is a single selection */
+        if (entries.size() == 1) {
+            LamiTableEntry entry = (LamiTableEntry) Iterables.getOnlyElement(entries);
+
+            /* Make sure the selection represent a time range */
+            LamiTimeRange timeRange = entry.getCorrespondingTimeRange();
+            if (timeRange == null) {
+                return;
+            }
+
+            /* Get the timestamps from the time range */
+            /**
+             * TODO: Consider low and high limits of timestamps here.
+             */
+            Number tsBeginValueNumber = timeRange.getBegin().getValue();
+            Number tsEndValueNumber = timeRange.getEnd().getValue();
+            if(tsBeginValueNumber == null || tsEndValueNumber == null) {
+                return;
+            }
+
+            /* Send Range update to other views */
+            ITmfTimestamp start = TmfTimestamp.fromNanos(tsBeginValueNumber.longValue());
+            ITmfTimestamp end = TmfTimestamp.fromNanos(tsEndValueNumber.longValue());
+            TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(this, start, end));
+        }
+
+    }
+
+    /**
+     * Signal handler for a trace selection range update signal. It will try to
+     * map the external selection to our lami table entry.
+     *
+     * @param signal
+     *            The received signal
+     */
+    @TmfSignalHandler
+    public void externalUpdateSelectionCustomCharts(TmfSelectionRangeUpdatedSignal signal) {
+        /* Make sure we are not sending a signal to ourself */
+        if (signal.getSource() == this) {
+            return;
+        }
+
+        TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime());
+
+        // Lami signal
+        Set<Integer> selections = getIndexOfEntriesIntersectingTimerange(fResultTable, range);
+
+        /* Update all LamiViewer */
+        LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selections, this);
+        TmfSignalManager.dispatchSignal(signal1);
+
+        // Custom chart signal
+        /* Find which lami table entry intersects the signal */
+        Set<Object> selection = getEntriesIntersectingTimerange(fResultTable, range);
+
+        /* Update all LamiViewer */
+        ChartSelectionUpdateSignal updateSignal = new ChartSelectionUpdateSignal(this, fResultTable, selection);
+        TmfSignalManager.dispatchSignal(updateSignal);
+    }
+
+    // ------------------------------------------------------------------------
+    // Util methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Util method that returns {@link LamiTableEntry} that intersects a
+     * {@link TmfTimeRange}.
+     *
+     * @param table
+     *            The result table to search for entries
+     * @param range
+     *            The time range itself
+     * @return The set of entries that intersect with the time range
+     */
+    private static Set<Object> getEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) {
+        Set<Object> entries = new HashSet<>();
+        for (LamiTableEntry entry : table.getEntries()) {
+            LamiTimeRange lamiTimeRange = entry.getCorrespondingTimeRange();
+
+            /* Make sure the table has time ranges */
+            if (lamiTimeRange == null) {
+                return entries;
+            }
+
+            /* Get the timestamps from the time range */
+            /**
+             * TODO: Consider low and high limits of timestamps here.
+             */
+            Number tsBeginValueNumber = lamiTimeRange.getBegin().getValue();
+            Number tsEndValueNumber = lamiTimeRange.getEnd().getValue();
+            if(tsBeginValueNumber == null || tsEndValueNumber == null) {
+                return entries;
+            }
+
+            /* Convert the timestamps into TMF timestamps */
+            ITmfTimestamp start = TmfTimestamp.fromNanos(tsBeginValueNumber.longValue());
+            ITmfTimestamp end = TmfTimestamp.fromNanos(tsEndValueNumber.longValue());
+
+            /* Add iff the time range intersects the the signal */
+            TmfTimeRange tempTimeRange = new TmfTimeRange(start, end);
+            if (tempTimeRange.getIntersection(range) != null) {
+                entries.add(entry);
+            }
+        }
+
+        return entries;
+    }
 }
This page took 0.02781 seconds and 5 git commands to generate.