/******************************************************************************* * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien * * 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 java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; 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.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect; import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType; import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription; import org.eclipse.ui.dialogs.SelectionDialog; /** * Series creation dialog * * @author Jonathan Rajotte-Julien */ public class LamiSeriesDialog extends SelectionDialog { private static final int MINIMUM_COLUMN_WIDTH = 30; private static final int MININAL_SERIES_TABLE_HEIGHT = 150; /* The root element to populate the viewer with */ private final Object fXInputElement; private final Object fYInputElement; private final List<LamiXYSeriesDescription> series; /* Providers for populating dialog */ private final ILabelProvider fXLabelProvider; private final IStructuredContentProvider fXContentProvider; private final ILabelProvider fYLabelProvider; private final IStructuredContentProvider fYContentProvider; private final IStructuredContentProvider fSeriesContentProvider; private final boolean fRestrictXSeriesNumbers; private final List<LamiAxisCheckBoxOption> fXCheckBoxOptions; private final List<LamiAxisCheckBoxOption> fYCheckBoxOptions; // the visual selection widget group private TableViewer fXTableViewer; private CheckboxTableViewer fYCheckBoxViewer; private TableViewer fSeriesListViewer; private Label fWarning; /** * @param parentShell * The parent shell of the dialog * @param chartType * The chart type for which the dialog construct series * @param xInput * The possible X axis set of values * @param yInput * The possible Y axis set of values * @param xContentProvider * A content provider for the X axis set * @param xLabelProvider * The label provider for the X axis set * @param yContentProvider * The content provider for the Y axis set * @param yLabelProvider * The label provider for the Y axis set */ public LamiSeriesDialog(Shell parentShell, ChartType chartType, Object xInput, Object yInput, IStructuredContentProvider xContentProvider, ILabelProvider xLabelProvider, IStructuredContentProvider yContentProvider, ILabelProvider yLabelProvider) { super(parentShell); fXInputElement = xInput; fYInputElement = yInput; fXContentProvider = xContentProvider; fXLabelProvider = xLabelProvider; fYContentProvider = yContentProvider; fYLabelProvider = yLabelProvider; series = new ArrayList<>(); fSeriesContentProvider = checkNotNull(ArrayContentProvider.getInstance()); fXCheckBoxOptions = new ArrayList<>(); fYCheckBoxOptions = new ArrayList<>(); fSeriesListViewer = new TableViewer(parentShell); fXTableViewer = new TableViewer(parentShell); fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(parentShell, SWT.NONE)); /* Dynamic restriction per chart type */ switch (chartType) { case XY_SCATTER: fRestrictXSeriesNumbers = false; break; case BAR_CHART: case PIE_CHART: default: fRestrictXSeriesNumbers = true; break; } this.fWarning = new Label(parentShell, SWT.NONE); } @Override protected Control createDialogArea(@Nullable Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); initializeDialogUnits(composite); /* Base 3 column grid layout */ GridLayout gridLayout = new GridLayout(3, false); composite.setLayout(gridLayout); GridData gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 3; Group seriesGroup = new Group(composite, SWT.NONE); seriesGroup.setLayoutData(gridData); seriesGroup.setLayout(new GridLayout(3, false)); seriesGroup.setText(Messages.LamiSeriesDialog_series); /* * New sub group for the series table. */ gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 2; gridData.heightHint = MININAL_SERIES_TABLE_HEIGHT; Group seriesTableGroup = new Group(seriesGroup, SWT.NONE); seriesTableGroup.setLayoutData(gridData); TableColumnLayout layout = new TableColumnLayout(); seriesTableGroup.setLayout(layout); /* Current series */ fSeriesListViewer = new TableViewer(seriesTableGroup, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); fSeriesListViewer.setContentProvider(fSeriesContentProvider); fSeriesListViewer.setInput(series); fSeriesListViewer.getTable().setHeaderVisible(true); fSeriesListViewer.getTable().setLinesVisible(true); TableViewerColumn column1 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_x_values, element -> element.getXAspect().getLabel()); TableViewerColumn column2 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_y_values, element -> element.getYAspect().getLabel()); layout.setColumnData(column1.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true)); layout.setColumnData(column2.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true)); /* Delete series button */ gridData = new GridData(GridData.CENTER); gridData.horizontalSpan = 1; Button deleteSeries = new Button(seriesGroup, SWT.PUSH); deleteSeries.setText(Messages.LamiSeriesDialog_delete); deleteSeries.setLayoutData(gridData); deleteSeries.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(@Nullable SelectionEvent e) { /* Remove the selectecd series */ IStructuredSelection selections = (IStructuredSelection) fSeriesListViewer.getSelection(); for (Object selection : selections.toList()) { series.remove(selection); } /* When table is empty reset to initial state */ if (series.isEmpty()) { /* Make sure the OK button is disabled */ getButton(IDialogConstants.OK_ID).setEnabled(false); /* Hide the selection warning */ fWarning.setVisible(false); /* * Reset the initial selection of the X axis selection table */ fXTableViewer.refresh(); /* Reset check boxes options */ fXCheckBoxOptions.forEach(checkBox -> { checkBox.setButtonEnabled(true); }); fYCheckBoxOptions.forEach(checkBox -> { checkBox.setButtonEnabled(true); }); } /* Refresh the series table to show the added series */ fSeriesListViewer.refresh(); } @Override public void widgetDefaultSelected(@Nullable SelectionEvent e) { } }); /* * Series creator subgroup */ gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 3; Group seriesCreatorGroup = new Group(composite, getShellStyle()); seriesCreatorGroup.setLayoutData(gridData); seriesCreatorGroup.setLayout(new GridLayout(3, false)); seriesCreatorGroup.setText(Messages.LamiSeriesDialog_serie_creator); /* X axis sash label */ gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END); gridData.horizontalSpan = 1; Label xSeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER); xSeriesCreatorLabel.setLayoutData(gridData); xSeriesCreatorLabel.setText(Messages.LamiSeriesDialog_x_axis); gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END); gridData.horizontalSpan = 1; Label ySeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER); ySeriesCreatorLabel.setLayoutData(gridData); ySeriesCreatorLabel.setText(Messages.LamiSeriesDialog_y_axis); /* Empty label for grid layout */ gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 1; Label emptyLabel = new Label(seriesCreatorGroup, SWT.CENTER); emptyLabel.setLayoutData(gridData); SashForm sash1 = new SashForm(seriesCreatorGroup, SWT.BORDER | SWT.HORIZONTAL); gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 2; sash1.setLayoutData(gridData); sash1.setVisible(true); fXTableViewer = new TableViewer(sash1, getTableStyle()); fXTableViewer.setContentProvider(fXContentProvider); fXTableViewer.setLabelProvider(fXLabelProvider); fXTableViewer.setInput(fXInputElement); fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(sash1, SWT.BORDER)); fYCheckBoxViewer.setLabelProvider(fYLabelProvider); fYCheckBoxViewer.setContentProvider(fYContentProvider); fYCheckBoxViewer.setInput(fYInputElement); gridData = new GridData(SWT.FILL, SWT.NONE, true, true); gridData.horizontalSpan = 1; Button button1 = new Button(seriesCreatorGroup, SWT.PUSH); button1.setText(Messages.LamiSeriesDialog_add); button1.setLayoutData(gridData); button1.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(@Nullable SelectionEvent e) { Object[] ySelections = fYCheckBoxViewer.getCheckedElements(); IStructuredSelection xSelections = (IStructuredSelection) fXTableViewer.getSelection(); @Nullable Object x = xSelections.getFirstElement(); if (!(x instanceof LamiTableEntryAspect) || ySelections.length == 0) { return; } /* Add selection to series if it doesn not already exist in the list */ for (Object y : ySelections) { if(!(y instanceof LamiTableEntryAspect)) { continue; } LamiXYSeriesDescription serie = new LamiXYSeriesDescription((LamiTableEntryAspect) x, ((LamiTableEntryAspect) y)); if (!series.contains(serie)) { series.add(serie); fSeriesListViewer.refresh(); } } /* Set label warning visible and enable OK button */ fWarning.setVisible(true); getButton(IDialogConstants.OK_ID).setEnabled(true); /* Update possible X selection based on current series */ TableItem[] items = fXTableViewer.getTable().getItems(); Arrays.stream(items).forEach(item -> { LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData(); if (!aspect.arePropertiesEqual(series.get(0).getXAspect())) { fXTableViewer.remove(aspect); } if (fRestrictXSeriesNumbers && aspect != (series.get(0).getXAspect())) { fXTableViewer.remove(aspect); } }); /* * Disable all checkBox that do not apply to aspects series. * Simply take the first one since all series should comply to * the same restriction */ fXCheckBoxOptions.forEach(checkBox -> { checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getXAspect())); }); fYCheckBoxOptions.forEach(checkBox -> { checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getYAspect())); }); } @Override public void widgetDefaultSelected(@Nullable SelectionEvent e) { } }); gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END); gridData.horizontalSpan = 3; fWarning = new Label(seriesCreatorGroup, SWT.LEFT); fWarning.setLayoutData(gridData); fWarning.setText(Messages.LamiSeriesDialog_selectionRestrictionWarning); fWarning.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); fWarning.setVisible(false); gridData = new GridData(GridData.FILL_BOTH); gridData.horizontalSpan = 3; Group optionGroups = new Group(composite, getShellStyle()); optionGroups.setLayoutData(gridData); optionGroups.setLayout(new GridLayout(3, false)); optionGroups.setText(Messages.LamiSeriesDialog_chart_options); for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) { Button button = new Button(optionGroups, SWT.CHECK); button.setSelection(checkBox.getDefaultValue()); button.setText(checkBox.getName()); checkBox.setButton(button); } for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) { Button button = new Button(optionGroups, SWT.CHECK); button.setSelection(checkBox.getDefaultValue()); button.setText(checkBox.getName()); checkBox.setButton(button); } fYCheckBoxViewer.getTable().addSelectionListener(new SelectionListener() { @Override public void widgetSelected(@Nullable SelectionEvent e) { /* On check */ if (e != null && e.detail == SWT.CHECK) { /* Change possible selection */ IStructuredSelection selections = (IStructuredSelection) fYCheckBoxViewer.getSelection(); if (selections.getFirstElement() != null) { boolean checked = fYCheckBoxViewer.getChecked(selections.getFirstElement()); /* * If just selected look for stuff to disable. If not no * need to look for stuff to disable since it was * already done before. */ if (checked) { TableItem[] items = fYCheckBoxViewer.getTable().getItems(); Arrays.stream(items).forEach(item -> { LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData(); if (!aspect.arePropertiesEqual((LamiTableEntryAspect) checkNotNull(selections.getFirstElement()))) { fYCheckBoxViewer.remove(aspect); } }); } else if (!checked && fYCheckBoxViewer.getCheckedElements().length == 0 && fSeriesListViewer.getTable().getItemCount() == 0) { fYCheckBoxViewer.refresh(); } } } } @Override public void widgetDefaultSelected(@Nullable SelectionEvent e) { } }); Dialog.applyDialogFont(composite); return composite; } /* * Disable OK button on dialog creation. */ @Override protected void createButtonsForButtonBar(@Nullable Composite parent) { super.createButtonsForButtonBar(parent); getButton(IDialogConstants.OK_ID).setEnabled(false); } /** * Return the style flags for the table viewer. * * @return int */ protected int getTableStyle() { return SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER; } /** * Add check box option for X series. * * @param name * The name of the option. The actual text shown to the user. * @param defaultValue * The default state of the check box option. * @param predicate * The predicate to check if the option applies to the given * aspect * @return The index of the option value in the result table. */ public int addXCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) { LamiAxisCheckBoxOption checkBox = new LamiAxisCheckBoxOption(name, defaultValue, predicate); fXCheckBoxOptions.add(checkBox); return fXCheckBoxOptions.size() - 1; } /** * Add check box option for Y series. * * @param name * The name of the option. The actual text shown to the user. * @param defaultValue * The default state of the check box option. * @param predicate * The predicate to check if the option applies to the given * aspect * @return The index of the option value in the result table. */ public int addYCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) { LamiAxisCheckBoxOption checkbox = new LamiAxisCheckBoxOption(name, defaultValue, predicate); fYCheckBoxOptions.add(checkbox); return fYCheckBoxOptions.size() - 1; } /** * @return The final values of X series check boxes. */ public boolean[] getXCheckBoxOptionValues() { boolean[] selections = new boolean[fXCheckBoxOptions.size()]; for (int i = 0; i < selections.length; i++) { selections[i] = fXCheckBoxOptions.get(i).getValue(); } return selections; } /** * @return The final values of Y series check boxes. */ public boolean[] getYCheckBoxOptionValues() { boolean[] selections = new boolean[fYCheckBoxOptions.size()]; for (int i = 0; i < selections.length; i++) { selections[i] = fYCheckBoxOptions.get(i).getValue(); } return selections; } @Override protected void okPressed() { for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) { checkBox.updateValue(); } for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) { checkBox.updateValue(); } super.okPressed(); } @Override public Object[] getResult() { return series.toArray(); } private static <T extends Comparable<T>> TableViewerColumn createTableViewerColumn(TableViewer viewer, String name, Function<LamiXYSeriesDescription, T> propertyFunction) { TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.CENTER); viewerColumn.setLabelProvider(new ColumnLabelProvider() { @Override public @Nullable String getText(@Nullable Object element) { if (element != null) { return propertyFunction.apply((LamiXYSeriesDescription) element).toString(); } return null; } }); TableColumn column = viewerColumn.getColumn(); column.setText(name); return viewerColumn; } }