blob: e5379659580c2c0e6dedefac73603a576a85c114 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2017 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.tmf.ui.tracetype.preferences;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.Messages;
import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper;
import org.eclipse.tracecompass.tmf.core.project.model.TraceTypePreferences;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.FilteredCheckboxTree;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.TreePatternFilter;
import org.eclipse.ui.dialogs.PatternFilter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* This class implements a preference page viewer for the trace type
*
* @author Jean-Christian Kouame
* @since 3.0
*/
public class TraceTypePreferencePageViewer {
private static final int BUTTON_CHECK_SELECTED_ID = IDialogConstants.CLIENT_ID;
private static final int BUTTON_UNCHECK_SELECTED_ID = IDialogConstants.CLIENT_ID + 1;
private static final String[] FILTER_COLUMN_NAMES = new String[] {
"Trace Types", //$NON-NLS-1$
"Initial Range Duration" //$NON-NLS-1$
};
private static final String[] COLUMN_PROPERTIES = new String[] {
"NAME", //$NON-NLS-1$
"DURATION" //$NON-NLS-1$
};
private static final Set<String> EDITABLE = ImmutableSet.of(Objects.requireNonNull(FILTER_COLUMN_NAMES[1]));
private boolean fIsEmpty;
private FilteredCheckboxTree fTree;
private TraceTypeTreeContentProvider fContentProvider;
private TraceTypeLabelProvider fLabelProvider;
private ViewerComparator fComparator;
private List<ViewerFilter> fFilters;
private Iterable<@NonNull TraceTypeHelper> fEntries;
private TextCellEditor fCellEditor;
/**
* Constructor
*
* @param entries
* The viewer entries
*/
public TraceTypePreferencePageViewer(Iterable<@NonNull TraceTypeHelper> entries) {
fEntries = entries;
fContentProvider = new TraceTypeTreeContentProvider();
fLabelProvider = new TraceTypeLabelProvider();
}
/**
* Create the filter viewer area and initialize the values
*
* @param parent
* The parent
* @return The created area
*/
public Composite create(Composite parent) {
Composite composite = createFilterArea(parent);
fIsEmpty = fEntries.iterator().hasNext();
BusyIndicator.showWhile(null, () -> {
Iterable<@NonNull TraceTypeHelper> toCheck = Iterables.filter(fEntries, helper -> helper.isEnabled());
toCheck.forEach(handler -> checkElement(handler));
fTree.getViewer().expandAll();
for (TreeColumn column : fTree.getViewer().getTree().getColumns()) {
column.pack();
}
fTree.getViewer().collapseAll();
});
return composite;
}
/**
* Create the filter area
*
* @param parent
* The parent composite
* @return The filter area composite
*/
public Composite createFilterArea(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(1, true));
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
CheckboxTreeViewer treeViewer = createTreeViewer(composite);
Control buttonComposite = createSelectionButtons(composite);
GridData data = new GridData(GridData.FILL_BOTH);
Tree treeWidget = treeViewer.getTree();
treeWidget.setLayoutData(data);
treeWidget.setFont(parent.getFont());
if (fIsEmpty) {
treeWidget.setEnabled(false);
buttonComposite.setEnabled(false);
}
return composite;
}
/**
* Creates the tree viewer.
*
* @param parent
* the parent composite
* @return the tree viewer
*/
protected CheckboxTreeViewer createTreeViewer(Composite parent) {
PatternFilter filter = new TreePatternFilter();
filter.setIncludeLeadingWildcard(true);
fTree = new FilteredCheckboxTree(parent, SWT.BORDER | SWT.MULTI, filter, true, false);
fTree.setLayout(new GridLayout());
fTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
TreeViewer viewer = fTree.getViewer();
TextCellEditor[] editors = new TextCellEditor[FILTER_COLUMN_NAMES.length];
TreeViewerEditor.create(viewer, new ColumnViewerEditorActivationStrategy(viewer) {
@Override
protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
if (event.getSource() instanceof ViewerCell) {
ViewerCell viewerCell = (ViewerCell) event.getSource();
if (editors[viewerCell.getColumnIndex()] != null) {
return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION;
}
}
return false;
}
}, ColumnViewerEditor.DEFAULT);
Tree tree = viewer.getTree();
fCellEditor = new TextCellEditor(tree, SWT.BORDER);
tree.setHeaderVisible(true);
for (String columnName : FILTER_COLUMN_NAMES) {
TreeColumn column = new TreeColumn(tree, SWT.LEFT);
column.setText(columnName);
}
for (int i = 0; i < FILTER_COLUMN_NAMES.length; i++) {
if (EDITABLE.contains(FILTER_COLUMN_NAMES[i])) {
editors[i] = fCellEditor;
}
}
viewer.setContentProvider(fContentProvider);
viewer.setLabelProvider(fLabelProvider);
viewer.setColumnProperties(COLUMN_PROPERTIES);
viewer.setCellEditors(editors);
viewer.setCellModifier(new ICellModifier() {
@Override
public void modify(Object element, String property, Object value) {
if (element instanceof TreeItem) {
TreeItem treeItem = (TreeItem) element;
Object data = treeItem.getData();
if (data instanceof TraceTypeHelper) {
TraceTypeHelper helper = (TraceTypeHelper) data;
if (property.equals(COLUMN_PROPERTIES[1])) {
try {
String text = value.toString().replaceAll("\\s", ""); //$NON-NLS-1$ //$NON-NLS-2$
double doubleval = Double.parseDouble(text) * 1e9;
TraceTypePreferences.setInitialTimeRange(helper.getTraceTypeId(), (long) doubleval);
viewer.refresh();
} catch (NumberFormatException e) {
// ignore me
}
}
}
}
}
@Override
public Object getValue(Object element, String property) {
if (element instanceof TraceTypeHelper) {
TraceTypeHelper helper = (TraceTypeHelper) element;
if (property.equals(COLUMN_PROPERTIES[1])) {
String traceTypeId = helper.getTraceTypeId();
return TmfTimestamp.fromNanos(TraceTypePreferences.getInitialTimeRange(traceTypeId, helper.getTrace().getInitialRangeOffset().toNanos())).toString(TmfTimestampFormat.getDefaulIntervalFormat());
}
}
return element;
}
@Override
public boolean canModify(Object element, String property) {
if (element instanceof TraceTypeHelper) {
return property.equals(COLUMN_PROPERTIES[1]);
}
return false;
}
});
fTree.addCheckStateListener(new CheckStateListener());
viewer.setComparator(fComparator);
if (fFilters != null) {
for (int i = 0; i != fFilters.size(); i++) {
viewer.addFilter(fFilters.get(i));
}
}
viewer.setInput(fEntries);
return (CheckboxTreeViewer) viewer;
}
/**
* Adds the selection and deselection buttons to the dialog.
*
* @param composite
* the parent composite
* @return Composite the composite the buttons were created in.
*/
protected Composite createSelectionButtons(Composite composite) {
Composite buttonComposite = new Composite(composite, SWT.NONE);
GridLayout layout = new GridLayout(2, true);
layout.marginWidth = 0;
buttonComposite.setLayout(layout);
buttonComposite.setFont(composite.getFont());
GridData data = new GridData(SWT.RIGHT, SWT.TOP, false, false);
buttonComposite.setLayoutData(data);
/* Create the buttons in the good order to place them as we want */
Button checkSelectedButton = createButton(buttonComposite,
BUTTON_CHECK_SELECTED_ID, Messages.TmfTimeFilterDialog_CHECK_SELECTED);
Button checkAllButton = createButton(buttonComposite,
IDialogConstants.SELECT_ALL_ID, Messages.TmfTimeFilterDialog_CHECK_ALL);
Button uncheckSelectedButton = createButton(buttonComposite,
BUTTON_UNCHECK_SELECTED_ID, Messages.TmfTimeFilterDialog_UNCHECK_SELECTED);
Button uncheckAllButton = createButton(buttonComposite,
IDialogConstants.DESELECT_ALL_ID, Messages.TmfTimeFilterDialog_UNCHECK_ALL);
/* Add a listener to each button */
checkSelectedButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
TreeSelection selection = (TreeSelection) fTree.getViewer().getSelection();
for (Object element : selection.toArray()) {
checkElement(element);
}
}
});
checkAllButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Object[] viewerElements = fContentProvider.getElements(fEntries);
for (int i = 0; i < viewerElements.length; i++) {
fTree.setSubtreeChecked(viewerElements[i], true);
}
}
});
uncheckSelectedButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
TreeSelection selection = (TreeSelection) fTree.getViewer().getSelection();
for (Object element : selection.toArray()) {
uncheckElement(element);
}
}
});
uncheckAllButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Object[] viewerElements = fContentProvider.getElements(fEntries);
for (Object element : viewerElements) {
if (fTree.getViewer().testFindItem(element) != null) {
// uncheck only visible roots and their children
uncheckElement(element);
}
}
}
});
return buttonComposite;
}
private static Button createButton(Composite parent, int id, String label) {
Button button = new Button(parent, SWT.PUSH);
button.setText(label);
button.setFont(JFaceResources.getDialogFont());
button.setData(Integer.valueOf(id));
GridData data = new GridData(SWT.FILL, SWT.CENTER, true, true);
button.setLayoutData(data);
return button;
}
/**
* Perform the default behavior
*/
public void performDefaults() {
Object input = fTree.getViewer().getInput();
if (input instanceof Iterable) {
((Iterable<?>) input).forEach(this::checkElementAndSubtree);
((Iterable<?>) input).forEach(element -> {
if(element instanceof TraceTypeHelper) {
TraceTypeHelper helper = (TraceTypeHelper) element;
TraceTypePreferences.resetInitialTimeRange(helper.getTraceTypeId());
}
});
}
fTree.getViewer().refresh();
fTree.getViewer().expandAll();
}
/**
* Private classes
*/
private class CheckStateListener implements ICheckStateListener {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
try {
boolean checked = event.getChecked();
if (checked) {
checkElement(event.getElement());
} else {
uncheckElement(event.getElement());
}
} catch (ClassCastException e) {
Activator.getDefault().logError("Failed to enable trace types", e); //$NON-NLS-1$
return;
}
}
}
/**
* Check an element and all its parents.
*
* @param element
* The element to check.
*/
private void checkElement(Object element) {
fTree.setChecked(element, true);
Object parent = fContentProvider.getParent(element);
while (parent != null && !fTree.getChecked(parent)) {
fTree.setChecked(parent, true);
parent = fContentProvider.getParent(parent);
}
Object[] children = fContentProvider.getChildren(element);
if (children != null) {
for (Object child : children) {
checkElement(child);
}
}
}
/**
* Check an element, all its parents and all its children.
*
* @param element
* The element to check.
*/
private void checkElementAndSubtree(Object element) {
checkElement(element);
for (Object child : fContentProvider.getChildren(element)) {
checkElementAndSubtree(child);
}
}
/**
* Uncheck an element and all its children.
*
* @param element
* The element to uncheck.
*/
private void uncheckElement(Object element) {
fTree.setChecked(element, false);
for (Object child : fContentProvider.getChildren(element)) {
uncheckElement(child);
}
Object parent = fContentProvider.getParent(element);
while (parent != null && !hasCheckedChild(parent)) {
fTree.setChecked(parent, false);
parent = fContentProvider.getParent(parent);
}
}
private boolean hasCheckedChild(Object parent) {
TraceTypeHelper[] children = fContentProvider.getChildren(parent);
for (Object child : children) {
if (fTree.getChecked(child)) {
return true;
}
}
return false;
}
/**
* Get the list of checked elements
*
* @return The checked elements
*/
public List<TraceTypeHelper> getCheckedElements() {
List<TraceTypeHelper> checked = new ArrayList<>();
Object[] checkedElements = fTree.getCheckedElements();
for (Object element : checkedElements) {
if (element instanceof TraceTypeHelper) {
checked.add((TraceTypeHelper) element);
}
}
return checked;
}
/**
* Get the list of unchecked elements
*
* @return The unchecked elements
*/
public List<TraceTypeHelper> getUncheckedElements() {
List<TraceTypeHelper> unchecked = getEntries();
unchecked.removeAll(getCheckedElements());
return unchecked;
}
/**
* Return a copy of the entries
*
* @return The copy of the entries
*/
private List<TraceTypeHelper> getEntries() {
return Lists.newArrayList(fEntries);
}
/**
* Sets the comparator used by the tree viewer.
*
* @param comparator
* The comparator
*/
public void setComparator(ViewerComparator comparator) {
fComparator = comparator;
}
}