org.lttng.scope.lami.ui.views.LamiSeriesDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.lttng.scope.lami.ui.views.LamiSeriesDialog.java

Source

/*******************************************************************************
 * 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.lttng.scope.lami.ui.views;

import static java.util.Objects.requireNonNull;
import static org.lttng.scope.common.core.NonNullUtils.nullToEmptyString;

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.ui.dialogs.SelectionDialog;
import org.lttng.scope.lami.core.aspect.LamiTableEntryAspect;
import org.lttng.scope.lami.core.module.LamiXYSeriesDescription;
import org.lttng.scope.lami.core.module.LamiChartModel.ChartType;

/**
 * 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 = requireNonNull(ArrayContentProvider.getInstance());

        fXCheckBoxOptions = new ArrayList<>();
        fYCheckBoxOptions = new ArrayList<>();
        fSeriesListViewer = new TableViewer(parentShell);
        fXTableViewer = new TableViewer(parentShell);
        fYCheckBoxViewer = requireNonNull(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,
                nullToEmptyString(Messages.LamiSeriesDialog_x_values), element -> element.getXAspect().getLabel());
        TableViewerColumn column2 = createTableViewerColumn(fSeriesListViewer,
                nullToEmptyString(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 = requireNonNull(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) requireNonNull(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;
    }

}