analysis.lami: Show one view per report
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Thu, 5 May 2016 01:05:56 +0000 (21:05 -0400)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 17 May 2016 23:32:15 +0000 (19:32 -0400)
Multiple tables in a report will be shown as multiple tabs
in the view. Each tab will have its own graphs. This will
allow naming the views with the report names, so it will be
easier to match a given view to its report.

Change-Id: Ieeef337079d385dfd79f3cc26a3574b800e7754c
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Reviewed-on: https://git.eclipse.org/r/72243
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-by: Hudson CI
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/OpenReportHandler.java
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/RunAnalysisHandler.java
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableViewer.java
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportView.java
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewFactory.java
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewTabPage.java [new file with mode: 0644]

index 9bceec72e6577aff0074c6afeac01fd9d8620ed6..5dec51f923dede811c92e2500afc2865a58252d6 100644 (file)
@@ -57,7 +57,7 @@ public class OpenReportHandler extends AbstractHandler {
 
             Display.getDefault().syncExec(() -> {
                 try {
-                    LamiReportViewFactory.createNewViews(lamiReport);
+                    LamiReportViewFactory.createNewView(lamiReport);
                 } catch (PartInitException e) {
                 }
             });
index 10458b1793fb977284f74538c7afbc79556789fb..e99c7cd605a53320b41da75e5f06f88cfef42dda 100644 (file)
@@ -131,7 +131,7 @@ public class RunAnalysisHandler extends AbstractHandler {
                     /* Automatically open the report for convenience */
                     Display.getDefault().syncExec(() -> {
                         try {
-                            LamiReportViewFactory.createNewViews(report);
+                            LamiReportViewFactory.createNewView(report);
                         } catch (PartInitException e) {
                         }
                     });
index 6bc712d05fe453bb799ae6c59368db96c4579e06..0c745961feb7557055b0a047848b23da90c5d213 100644 (file)
@@ -194,6 +194,10 @@ public final class LamiTableViewer extends TmfSimpleTableViewer implements ILami
             }
         });
         Display.getDefault().asyncExec(() -> {
+            if (tableViewer.getTable().isDisposed()) {
+                return;
+            }
+
             TableColumn[] cols = tableViewer.getTable().getColumns();
             for (int i = 0; i < cols.length; i++) {
                 cols[i].pack();
index ad7e68db50b3bbadcfa84c7727c1217afee3d474..ed38dd6c26f529bea50e8f2c33a9785208b4ddbf 100644 (file)
 package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
 
 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
-import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.viewers.ArrayContentProvider;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.LabelProvider;
-import org.eclipse.jface.window.Window;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
 import org.eclipse.swt.custom.SashForm;
 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.analysis.lami.ui.Activator;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysisReport;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType;
 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.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
-import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
-import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
-import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
-import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
 import org.eclipse.tracecompass.tmf.ui.views.TmfView;
 
-import com.google.common.collect.Iterables;
-
 /**
  * Base view showing output of Babeltrace scripts.
  *
@@ -73,13 +48,43 @@ public final class LamiReportView extends TmfView {
     /** View ID */
     public static final String VIEW_ID = "org.eclipse.tracecompass.analysis.lami.views.reportview"; //$NON-NLS-1$
 
-    private final @Nullable LamiResultTable fResultTable;
+    private final @Nullable LamiAnalysisReport fReport;
+    private final List<LamiReportViewTabPage> fTabPages;
+
+    private @Nullable CTabFolder fTabFolder;
+
+    // ------------------------------------------------------------------------
+    // Actions
+    // ------------------------------------------------------------------------
+
+    private class ToggleTableAction extends Action {
+        @Override
+        public void run() {
+            LamiReportViewTabPage page = getCurrentSelectedPage();
+            if (page == null) {
+                return;
+            }
+            page.toggleTableViewer();
+        }
+    }
+
+    private class NewChartAction extends Action {
+
+        private final ChartType fChartType;
 
-    private @Nullable LamiViewerControl fTableViewerControl;
-    private final Set<LamiViewerControl> fPredefGraphViewerControls = new LinkedHashSet<>();
-    private final Set<LamiViewerControl> fCustomGraphViewerControls = new LinkedHashSet<>();
-    private @Nullable SashForm fSashForm;
-    private Set<Integer> fSelectionIndexes;
+        public NewChartAction(ChartType chartType) {
+            fChartType = chartType;
+        }
+
+        @Override
+        public void run() {
+            LamiReportViewTabPage page = getCurrentSelectedPage();
+            if (page == null) {
+                return;
+            }
+            page.createNewCustomChart(fChartType);
+        }
+    }
 
     // ------------------------------------------------------------------------
     // Constructor
@@ -90,11 +95,8 @@ public final class LamiReportView extends TmfView {
      */
     public LamiReportView() {
         super(VIEW_ID);
-        fResultTable = LamiReportViewFactory.getCurrentResultTable();
-        fSelectionIndexes = new HashSet<>();
-        if (fResultTable != null) {
-            fSelectionIndexes = getIndexOfEntriesIntersectingTimerange(checkNotNull(fResultTable), TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange());
-        }
+        fReport = LamiReportViewFactory.getCurrentReport();
+        fTabPages = new ArrayList<>();
     }
 
     // ------------------------------------------------------------------------
@@ -103,36 +105,39 @@ public final class LamiReportView extends TmfView {
 
     @Override
     public void createPartControl(@Nullable Composite parent) {
-        LamiResultTable resultTable = fResultTable;
-        if (resultTable == null || parent == null) {
+        LamiAnalysisReport report = fReport;
+        if (report == null || parent == null) {
             return;
         }
 
-        SashForm sf = new SashForm(parent, SWT.NONE);
-        fSashForm = sf;
-        setPartName(resultTable.getTableClass().getTableTitle());
+        setPartName(report.getName());
 
-        /* Prepare the table viewer, which is always present */
-        LamiViewerControl tableViewerControl = new LamiViewerControl(sf, resultTable);
-        fTableViewerControl = tableViewerControl;
+        fTabFolder = new CTabFolder(parent, SWT.NONE);
+        fTabFolder.setSimple(false);
 
-        /* Prepare the predefined graph viewers, if any */
-        resultTable.getTableClass().getPredefinedViews()
-            .forEach(graphModel -> fPredefGraphViewerControls.add(new LamiViewerControl(sf, resultTable, graphModel)));
+        for (LamiResultTable table : report.getTables()) {
+            String name = table.getTableClass().getTableTitle();
 
-        /* Automatically open the table viewer initially */
-        tableViewerControl.getToggleAction().run();
+            CTabItem tabItem = new CTabItem(fTabFolder, SWT.NULL);
+            tabItem.setText(name);
+
+            SashForm sf = new SashForm(fTabFolder, SWT.NONE);
+            fTabPages.add(new LamiReportViewTabPage(sf, table));
+            tabItem.setControl(sf);
+        }
 
         /* Add toolbar buttons */
+        Action toggleTableAction = new ToggleTableAction();
+        toggleTableAction.setText(Messages.LamiReportView_ActivateTableAction_ButtonName);
+        toggleTableAction.setToolTipText(Messages.LamiReportView_ActivateTableAction_ButtonTooltip);
+        toggleTableAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath("icons/table.gif")); //$NON-NLS-1$
+
         IToolBarManager toolbarMgr = getViewSite().getActionBars().getToolBarManager();
-        toolbarMgr.add(tableViewerControl.getToggleAction());
-        fPredefGraphViewerControls.stream()
-            .map(LamiViewerControl::getToggleAction)
-            .forEach(toolbarMgr::add);
+        toolbarMgr.add(toggleTableAction);
 
         IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager();
-        IAction newBarChartAction = new NewChartAction(checkNotNull(parent.getShell()), sf, resultTable, ChartType.BAR_CHART);
-        IAction newXYScatterAction = new NewChartAction(checkNotNull(parent.getShell()), sf, resultTable, ChartType.XY_SCATTER);
+        IAction newBarChartAction = new NewChartAction(ChartType.BAR_CHART);
+        IAction newXYScatterAction = new NewChartAction(ChartType.XY_SCATTER);
 
         newBarChartAction.setText(Messages.LamiReportView_NewCustomBarChart);
         newXYScatterAction.setText(Messages.LamiReportView_NewCustomScatterChart);
@@ -141,10 +146,12 @@ public final class LamiReportView extends TmfView {
         IAction clearCustomViewsAction = new Action() {
             @Override
             public void run() {
-                fCustomGraphViewerControls.forEach(LamiViewerControl::dispose);
-                fCustomGraphViewerControls.clear();
-                sf.layout();
-
+                LamiReportViewTabPage tabPage = getCurrentSelectedPage();
+                if (tabPage == null) {
+                    return;
+                }
+                tabPage.clearAllCustomViewers();
+                tabPage.getControl().layout();
             }
         };
         clearCustomViewsAction.setText(Messages.LamiReportView_ClearAllCustomViews);
@@ -154,9 +161,11 @@ public final class LamiReportView extends TmfView {
         menuMgr.add(new Separator());
         menuMgr.add(clearCustomViewsAction);
 
-        /* Simulate a new external signal to the default viewer */
-        LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportView.this, fSelectionIndexes, checkNotNull(fResultTable).hashCode());
-        TmfSignalManager.dispatchSignal(signal);
+        /* Select the first tab initially */
+        CTabFolder tf = checkNotNull(fTabFolder);
+        if (tf.getItemCount() > 0) {
+            tf.setSelection(0);
+        }
     }
 
     // ------------------------------------------------------------------------
@@ -165,292 +174,18 @@ public final class LamiReportView extends TmfView {
 
     @Override
     public void setFocus() {
-    }
-
-    @Override
-    public void dispose() {
-        super.dispose();
-        if (fSashForm != null) {
-            fSashForm.dispose();
+        if (fTabFolder != null) {
+            fTabFolder.setFocus();
         }
-        if (fTableViewerControl != null) {
-            fTableViewerControl.dispose();
-        }
-        fPredefGraphViewerControls.forEach(LamiViewerControl::dispose);
-        fCustomGraphViewerControls.forEach(LamiViewerControl::dispose);
     }
 
-    private class NewChartAction extends Action {
-
-        private final Shell icfDialogParentShell;
-        private final Composite icfChartViewerParent;
-        private final LamiResultTable icfResultTable;
-        private boolean icfXLogScale;
-        private boolean icfYLogScale;
-        private final ChartType icfChartType;
-
-        public NewChartAction(Shell parentShell, Composite chartViewerParent,
-                LamiResultTable resultTable, ChartType chartType) {
-            icfDialogParentShell = parentShell;
-            icfChartViewerParent = chartViewerParent;
-            icfResultTable = resultTable;
-            icfXLogScale = false;
-            icfYLogScale = false;
-            icfChartType = chartType;
-        }
-
-        @Override
-        public void run() {
-            int xLogScaleOptionIndex = -1;
-            int yLogScaleOptionIndex = -1;
-
-            List<LamiTableEntryAspect> xStringColumn = icfResultTable.getTableClass().getAspects().stream()
-                    .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
-                    .collect(Collectors.toList());
-
-            /* Get the flattened aspects for Y since mapping an aggregate aspect to y series make no sense so far */
-            List<LamiTableEntryAspect> yStringColumn = icfResultTable.getTableClass().getAspects().stream()
-                    .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
-                    .collect(Collectors.toList());
-
-            switch (icfChartType) {
-            case BAR_CHART:
-                /* Y value must strictly continous and non timestamp */
-                yStringColumn = yStringColumn.stream()
-                    .filter(aspect -> !aspect.isTimeStamp() && aspect.isContinuous())
-                    .collect(Collectors.toList());
-                break;
-            case PIE_CHART:
-                break;
-            case XY_SCATTER:
-                break;
-            default:
-                break;
-            }
-
-            IStructuredContentProvider contentProvider = checkNotNull(ArrayContentProvider.getInstance());
-
-            LamiSeriesDialog dialog = new LamiSeriesDialog(icfDialogParentShell,
-                    icfChartType,
-                    xStringColumn,
-                    yStringColumn,
-                    contentProvider,
-                    new LabelProvider() {
-                        @Override
-                        public String getText(@Nullable Object element) {
-                            return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
-                        }
-                    },
-                    contentProvider,
-                    new LabelProvider() {
-                        @Override
-                        public String getText(@Nullable Object element) {
-                            return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
-                        }
-                    });
-            dialog.setTitle(icfChartType.toString() + ' ' + Messages.LamiSeriesDialog_creation);
-
-            /* X options per chart type */
-            switch (icfChartType) {
-            case XY_SCATTER:
-                xLogScaleOptionIndex = dialog.addXCheckBoxOption(
-                        Messages.LamiSeriesDialog_x_axis + ' ' + Messages.LamiReportView_LogScale,
-                        false, new Predicate<LamiTableEntryAspect>() {
-                    @Override
-                    public boolean test(@NonNull LamiTableEntryAspect t) {
-                        return t.isContinuous() && !t.isTimeStamp();
-                    }
-                });
-                break;
-            case BAR_CHART:
-            case PIE_CHART:
-            default:
-                break;
-            }
-
-            /* Y options per chart type */
-            switch (icfChartType) {
-            case BAR_CHART:
-            case XY_SCATTER:
-                yLogScaleOptionIndex = dialog.addYCheckBoxOption(
-                        Messages.LamiSeriesDialog_y_axis + ' ' + Messages.LamiReportView_LogScale,
-                        false, new Predicate<LamiTableEntryAspect>() {
-                    @Override
-                    public boolean test(@NonNull LamiTableEntryAspect t) {
-                        return t.isContinuous() && !t.isTimeStamp();
-                    }
-                });
-                break;
-
-            case PIE_CHART:
-            default:
-                break;
-            }
-
-            if (dialog.open() != Window.OK) {
-                return;
-            }
-
-            List<LamiXYSeriesDescription> results = Arrays.stream(dialog.getResult())
-                    .map(serie -> (LamiXYSeriesDescription) serie)
-                    .collect(Collectors.toList());
-
-            boolean[] xCheckBoxOptionsResults = dialog.getXCheckBoxOptionValues();
-            boolean[] yCheckBoxOptionsResults = dialog.getYCheckBoxOptionValues();
-
-            /* Get X log scale option */
-            if (xLogScaleOptionIndex > -1 && xLogScaleOptionIndex < xCheckBoxOptionsResults.length) {
-                icfXLogScale = xCheckBoxOptionsResults[xLogScaleOptionIndex];
-            }
-            /* Get Y log scale option */
-            if (yLogScaleOptionIndex > -1 && yLogScaleOptionIndex < yCheckBoxOptionsResults.length) {
-                icfYLogScale = yCheckBoxOptionsResults[yLogScaleOptionIndex];
-            }
-
-            List<String> xAxisColString = new ArrayList<>();
-            List<String> yAxisColString = new ArrayList<>();
-
-            /* Specific chart type result fetching */
-            switch (icfChartType) {
-            case PIE_CHART:
-            case BAR_CHART:
-                /* Validate that we only have 1 X aspect */
-                if (results.stream()
-                        .map(element -> element.getXAspect().getLabel())
-                        .distinct()
-                        .count() != 1) {
-                    throw new IllegalStateException("No unique X axis label for results"); //$NON-NLS-1$
-                }
-                xAxisColString = results.stream()
-                        .map(element -> element.getXAspect().getLabel())
-                        .distinct()
-                        .collect(Collectors.toList());
-                break;
-            case XY_SCATTER:
-                xAxisColString = results.stream()
-                        .map(element -> element.getXAspect().getLabel())
-                        .collect(Collectors.toList());
-                break;
-            default:
-                break;
-            }
-
-            yAxisColString = results.stream()
-                    .map(element -> element.getYAspect().getLabel())
-                    .collect(Collectors.toList());
-
-            LamiChartModel model = new LamiChartModel(icfChartType,
-                    nullToEmptyString(Messages.LamiReportView_Custom),
-                    xAxisColString,
-                    yAxisColString,
-                    icfXLogScale,
-                    icfYLogScale);
-
-            LamiViewerControl viewerControl = new LamiViewerControl(icfChartViewerParent, icfResultTable, model);
-            fCustomGraphViewerControls.add(viewerControl);
-            viewerControl.getToggleAction().run();
-
-            /* Signal the current selection to the newly created graph */
-            LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportView.this, fSelectionIndexes, checkNotNull(fResultTable).hashCode());
-            TmfSignalManager.dispatchSignal(signal);
+    private @Nullable LamiReportViewTabPage getCurrentSelectedPage() {
+        CTabFolder tf = fTabFolder;
+        if (tf == null) {
+            return null;
         }
+        int idx = tf.getSelectionIndex();
+        return fTabPages.get(idx);
     }
 
-    // ------------------------------------------------------------------------
-    // Signals
-    // ------------------------------------------------------------------------
-
-    /**
-     * Signal handler for selection update.
-     * Propagate a TmfSelectionRangeUpdatedSignal if possible.
-     *
-     * @param signal
-     *          The selection update signal
-     */
-    @TmfSignalHandler
-    public void updateSelection(LamiSelectionUpdateSignal signal) {
-        LamiResultTable table = fResultTable;
-        if (table == null) {
-            return;
-        }
-
-        if (table.hashCode() != signal.getSignalHash() || equals(signal.getSource())) {
-            /* The signal is not for us */
-            return;
-        }
-
-        Set<Integer> entryIndex = signal.getEntryIndex();
-
-        /*
-         * Since most of the external viewers deal only with continuous time
-         * ranges and do not allow multi-time range selection, simply signal
-         * only when one selection is present.
-         */
-
-        if (entryIndex.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.
-             */
-            return;
-        }
-
-        if (entryIndex.size() == 1) {
-            int index = Iterables.getOnlyElement(entryIndex).intValue();
-            LamiTimeRange timeRange = table.getEntries().get(index).getCorrespondingTimeRange();
-            if (timeRange != null) {
-                /* Send Range update to other views */
-                ITmfTimestamp start = TmfTimestamp.fromNanos(timeRange.getStart());
-                ITmfTimestamp end = TmfTimestamp.fromNanos(timeRange.getEnd());
-                TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(LamiReportView.this, start, end));
-            }
-        }
-
-        fSelectionIndexes = entryIndex;
-    }
-
-    /**
-     * Signal handler for time range selections
-     *
-     * @param signal
-     *            The received signal
-     */
-    @TmfSignalHandler
-    public void externalUpdateSelection(TmfSelectionRangeUpdatedSignal signal) {
-        LamiResultTable table = fResultTable;
-        if (table == null) {
-            return;
-        }
-
-        if (signal.getSource() == this) {
-            /* We are the source */
-            return;
-        }
-        TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime());
-
-        Set<Integer> selections = getIndexOfEntriesIntersectingTimerange(table, range);
-
-        /* Update all LamiViewer */
-        LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportView.this, selections, table.hashCode());
-        TmfSignalManager.dispatchSignal(signal1);
-    }
-
-    private static Set<Integer> getIndexOfEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) {
-        Set<Integer> selections = new HashSet<>();
-        for (LamiTableEntry entry : table.getEntries()) {
-            LamiTimeRange timerange = entry.getCorrespondingTimeRange();
-            if (timerange == null) {
-                /* Return since the table have no timerange */
-                return selections;
-            }
-
-            TmfTimeRange tempTimeRange = new TmfTimeRange(TmfTimestamp.fromNanos(timerange.getStart()), TmfTimestamp.fromNanos(timerange.getEnd()));
-            if (tempTimeRange.getIntersection(range) != null) {
-                selections.add(table.getEntries().indexOf(entry));
-            }
-        }
-        return selections;
-    }
 }
index d768dc88729e24cf1b55830f6377b388777a1cf3..a6f51a190dff11987ac5dd339c6add555edb7424 100644 (file)
@@ -11,7 +11,6 @@ package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysisReport;
-import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
@@ -28,16 +27,17 @@ public final class LamiReportViewFactory {
     private LamiReportViewFactory() {
     }
 
-    private static @Nullable LamiResultTable currentTable;
+    private static @Nullable LamiAnalysisReport currentReport;
     private static int secondaryViewId = 1;
 
     /**
-     * Return the current result table
+     * Return the current report. Should be accessed by the view currently being
+     * built.
      *
-     * @return The current result table
+     * @return The current report
      */
-    public static @Nullable LamiResultTable getCurrentResultTable() {
-        return currentTable;
+    public static @Nullable LamiAnalysisReport getCurrentReport() {
+        return currentReport;
     }
 
     /**
@@ -48,21 +48,24 @@ public final class LamiReportViewFactory {
      * @throws PartInitException
      *             If there was a problem initializing a view
      */
-    public static synchronized void createNewViews(LamiAnalysisReport report) throws PartInitException {
-        boolean firstView = true;
+    public static synchronized void createNewView(LamiAnalysisReport report)
+            throws PartInitException {
+        currentReport = report;
 
-        for (LamiResultTable table : report.getTables()) {
-            currentTable = table;
+        final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
 
-            int mode = (firstView ? IWorkbenchPage.VIEW_ACTIVATE : IWorkbenchPage.VIEW_VISIBLE);
+        /*
+         * Doing this in two operations here, instead of using
+         * IWorkbenchPage.VIEW_ACTIVATE, works around a bug where the contextual
+         * menu would get "stuck" until the Project view is defocused and
+         * refocused.
+         */
+        page.showView(LamiReportView.VIEW_ID, String.valueOf(secondaryViewId), IWorkbenchPage.VIEW_VISIBLE);
+        page.activate(page.findView(LamiReportView.VIEW_ID));
 
-            final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
-            page.showView(LamiReportView.VIEW_ID, String.valueOf(secondaryViewId), mode);
-            secondaryViewId++;
+        secondaryViewId++;
 
-            currentTable = null;
-            firstView = false;
-        }
+        currentReport = null;
     }
 
 }
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewTabPage.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewTabPage.java
new file mode 100644 (file)
index 0000000..1d95830
--- /dev/null
@@ -0,0 +1,422 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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.analysis.lami.ui.views;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+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.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.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.tmf.core.component.TmfComponent;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Sub-view of a {@link LamiReportView} that shows the contents of one table of
+ * the analysis report. While it is not a View object directly, its
+ * responsibilities are the same.
+ *
+ * @author Alexandre Montplaisir
+ * @author Jonathan Rajotte-Julien
+ */
+public final class LamiReportViewTabPage extends TmfComponent {
+
+    // ------------------------------------------------------------------------
+    // Attributes
+    // ------------------------------------------------------------------------
+
+    private final LamiResultTable fResultTable;
+
+    private final LamiViewerControl fTableViewerControl;
+    private final Set<LamiViewerControl> fCustomGraphViewerControls = new LinkedHashSet<>();
+    private final Composite fControl;
+
+    private Set<Integer> fSelectionIndexes;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructor
+     *
+     * @param parent
+     *            Parent composite
+     * @param table
+     *            The result table to display in this tab
+     */
+    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;
+
+        /* Prepare the table viewer, which is always present */
+        LamiViewerControl tableViewerControl = new LamiViewerControl(fControl, fResultTable);
+        fTableViewerControl = tableViewerControl;
+
+        /* Automatically open the table viewer initially */
+        tableViewerControl.getToggleAction().run();
+
+        /* Simulate a new external signal to the default viewer */
+        LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, fSelectionIndexes, checkNotNull(fResultTable).hashCode());
+        TmfSignalManager.dispatchSignal(signal);
+
+        fControl.addDisposeListener(e -> {
+            /* Dispose this class's resource */
+            fTableViewerControl.dispose();
+            clearAllCustomViewers();
+            super.dispose();
+        });
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void dispose() {
+        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;
+    }
+
+    /**
+     * Get the result table shown in this tab.
+     *
+     * @return The report result table
+     */
+    public LamiResultTable getResultTable() {
+        return fResultTable;
+    }
+
+    /**
+     * Clear all the custom graph viewers in this tab.
+     */
+    public void clearAllCustomViewers() {
+        fCustomGraphViewerControls.forEach(LamiViewerControl::dispose);
+        fCustomGraphViewerControls.clear();
+    }
+
+    /**
+     * Toggle the display of the table viewer in this tab. This shows it if it
+     * is currently hidden, and vice versa.
+     */
+    public void toggleTableViewer() {
+        fTableViewerControl.getToggleAction().run();
+    }
+
+    /**
+     * Add a new chart viewer to this tab.
+     *
+     * The method only needs a chart type (currently selected via separate
+     * actions), all other information will be found in the result table or in
+     * dialogs shown to the user as part of the execution of this method.
+     *
+     * @param chartType
+     *            The type of chart to create
+     */
+    public void createNewCustomChart(ChartType chartType) {
+        int xLogScaleOptionIndex = -1;
+        int yLogScaleOptionIndex = -1;
+
+        List<LamiTableEntryAspect> xStringColumn = fResultTable.getTableClass().getAspects().stream()
+                .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
+                .collect(Collectors.toList());
+
+        /* Get the flattened aspects for Y since mapping an aggregate aspect to y series make no sense so far */
+        List<LamiTableEntryAspect> yStringColumn = fResultTable.getTableClass().getAspects().stream()
+                .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
+                .collect(Collectors.toList());
+
+        switch (chartType) {
+        case BAR_CHART:
+            /* Y value must strictly continous and non timestamp */
+            yStringColumn = yStringColumn.stream()
+                .filter(aspect -> !aspect.isTimeStamp() && aspect.isContinuous())
+                .collect(Collectors.toList());
+            break;
+        case PIE_CHART:
+            break;
+        case XY_SCATTER:
+            break;
+        default:
+            break;
+        }
+
+        IStructuredContentProvider contentProvider = checkNotNull(ArrayContentProvider.getInstance());
+
+        LamiSeriesDialog dialog = new LamiSeriesDialog(getControl().getShell(),
+                chartType,
+                xStringColumn,
+                yStringColumn,
+                contentProvider,
+                new LabelProvider() {
+                    @Override
+                    public String getText(@Nullable Object element) {
+                        return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
+                    }
+                },
+                contentProvider,
+                new LabelProvider() {
+                    @Override
+                    public String getText(@Nullable Object element) {
+                        return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
+                    }
+                });
+        dialog.setTitle(chartType.toString() + ' ' + Messages.LamiSeriesDialog_creation);
+
+        /* X options per chart type */
+        switch (chartType) {
+        case XY_SCATTER:
+            xLogScaleOptionIndex = dialog.addXCheckBoxOption(
+                    Messages.LamiSeriesDialog_x_axis + ' ' + Messages.LamiReportView_LogScale,
+                    false, new Predicate<LamiTableEntryAspect>() {
+                @Override
+                public boolean test(@NonNull LamiTableEntryAspect t) {
+                    return t.isContinuous() && !t.isTimeStamp();
+                }
+            });
+            break;
+        case BAR_CHART:
+        case PIE_CHART:
+        default:
+            break;
+        }
+
+        /* Y options per chart type */
+        switch (chartType) {
+        case BAR_CHART:
+        case XY_SCATTER:
+            yLogScaleOptionIndex = dialog.addYCheckBoxOption(
+                    Messages.LamiSeriesDialog_y_axis + ' ' + Messages.LamiReportView_LogScale,
+                    false, new Predicate<LamiTableEntryAspect>() {
+                @Override
+                public boolean test(@NonNull LamiTableEntryAspect t) {
+                    return t.isContinuous() && !t.isTimeStamp();
+                }
+            });
+            break;
+
+        case PIE_CHART:
+        default:
+            break;
+        }
+
+        if (dialog.open() != Window.OK) {
+            return;
+        }
+
+        List<LamiXYSeriesDescription> results = Arrays.stream(dialog.getResult())
+                .map(serie -> (LamiXYSeriesDescription) serie)
+                .collect(Collectors.toList());
+
+        boolean[] xCheckBoxOptionsResults = dialog.getXCheckBoxOptionValues();
+        boolean[] yCheckBoxOptionsResults = dialog.getYCheckBoxOptionValues();
+
+        boolean isXLogScale = false;
+        boolean isYLogScale = false;
+
+        /* Get X log scale option */
+        if (xLogScaleOptionIndex > -1 && xLogScaleOptionIndex < xCheckBoxOptionsResults.length) {
+            isXLogScale = xCheckBoxOptionsResults[xLogScaleOptionIndex];
+        }
+        /* Get Y log scale option */
+        if (yLogScaleOptionIndex > -1 && yLogScaleOptionIndex < yCheckBoxOptionsResults.length) {
+            isYLogScale = yCheckBoxOptionsResults[yLogScaleOptionIndex];
+        }
+
+        List<String> xAxisColString = new ArrayList<>();
+        List<String> yAxisColString = new ArrayList<>();
+
+        /* Specific chart type result fetching */
+        switch (chartType) {
+        case PIE_CHART:
+        case BAR_CHART:
+            /* Validate that we only have 1 X aspect */
+            if (results.stream()
+                    .map(element -> element.getXAspect().getLabel())
+                    .distinct()
+                    .count() != 1) {
+                throw new IllegalStateException();
+            }
+            xAxisColString = results.stream()
+                    .map(element -> element.getXAspect().getLabel())
+                    .distinct()
+                    .collect(Collectors.toList());
+            break;
+        case XY_SCATTER:
+            xAxisColString = results.stream()
+                    .map(element -> element.getXAspect().getLabel())
+                    .collect(Collectors.toList());
+            break;
+        default:
+            break;
+        }
+
+        yAxisColString = results.stream()
+                .map(element -> element.getYAspect().getLabel())
+                .collect(Collectors.toList());
+
+        LamiChartModel model = new LamiChartModel(chartType,
+                nullToEmptyString(Messages.LamiReportView_Custom),
+                xAxisColString,
+                yAxisColString,
+                isXLogScale,
+                isYLogScale);
+
+        LamiViewerControl viewerControl = new LamiViewerControl(fControl, fResultTable, model);
+        fCustomGraphViewerControls.add(viewerControl);
+        viewerControl.getToggleAction().run();
+
+        /* Signal the current selection to the newly created graph */
+        LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this,
+                fSelectionIndexes, checkNotNull(fResultTable).hashCode());
+        TmfSignalManager.dispatchSignal(signal);
+    }
+
+    // ------------------------------------------------------------------------
+    // Signals
+    // ------------------------------------------------------------------------
+
+    /**
+     * Signal handler for selection update.
+     * Propagate a TmfSelectionRangeUpdatedSignal if possible.
+     *
+     * @param signal
+     *          The selection update signal
+     */
+    @TmfSignalHandler
+    public void updateSelection(LamiSelectionUpdateSignal signal) {
+        LamiResultTable table = fResultTable;
+        Object source = signal.getSource();
+
+        if (table.hashCode() != signal.getSignalHash() ||
+                source == this ||
+                /*
+                 * Don't forward signals from other tab pages, especially those
+                 * from other views.
+                 */
+                source instanceof LamiReportViewTabPage) {
+            /* The signal is not for us */
+            return;
+        }
+
+        Set<Integer> entryIndex = signal.getEntryIndex();
+
+        /*
+         * 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 (entryIndex.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.
+             */
+            return;
+        }
+
+        if (entryIndex.size() == 1) {
+            int index = Iterables.getOnlyElement(entryIndex).intValue();
+            LamiTimeRange timeRange = table.getEntries().get(index).getCorrespondingTimeRange();
+            if (timeRange != null) {
+                /* Send Range update to other views */
+                ITmfTimestamp start = TmfTimestamp.fromNanos(timeRange.getStart());
+                ITmfTimestamp end = TmfTimestamp.fromNanos(timeRange.getEnd());
+                TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(LamiReportViewTabPage.this, start, end));
+            }
+        }
+
+        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;
+        }
+        TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime());
+
+        Set<Integer> selections = getIndexOfEntriesIntersectingTimerange(table, range);
+
+        /* Update all LamiViewer */
+        LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportViewTabPage.this, selections, table.hashCode());
+        TmfSignalManager.dispatchSignal(signal1);
+    }
+
+    private static Set<Integer> getIndexOfEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) {
+        Set<Integer> selections = new HashSet<>();
+        for (LamiTableEntry entry : table.getEntries()) {
+            LamiTimeRange timerange = entry.getCorrespondingTimeRange();
+            if (timerange == null) {
+                /* Return since the table have no timerange */
+                return selections;
+            }
+
+            TmfTimeRange tempTimeRange = new TmfTimeRange(TmfTimestamp.fromNanos(timerange.getStart()), TmfTimestamp.fromNanos(timerange.getEnd()));
+            if (tempTimeRange.getIntersection(range) != null) {
+                selections.add(table.getEntries().indexOf(entry));
+            }
+        }
+        return selections;
+    }
+}
This page took 0.038073 seconds and 5 git commands to generate.