/*******************************************************************************
- * Copyright (c) 2012, 2015 Ericsson, others
+ * Copyright (c) 2012, 2016 Ericsson, others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
-import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ShowFilterDialogAction;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphMarkerAxis;
import com.google.common.collect.Iterables;
// Constants
// ------------------------------------------------------------------------
- /** Constant indicating that all levels of the time graph should be expanded */
+ /**
+ * Constant indicating that all levels of the time graph should be expanded
+ */
public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
private static final Object FILLER = new Object();
private final Map<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<>();
/** The map of viewer filters to viewer filter wrappers */
- private final Map<ViewerFilter, ViewerFilter> fViewerFilterMap = new HashMap<>();
+ private final Map<@NonNull ViewerFilter, @NonNull ViewerFilter> fViewerFilterMap = new HashMap<>();
/**
* Flag to block the tree selection changed listener when triggered by the
private Listener fSashDragListener;
private SashForm fSashForm;
+ private final boolean fScrollBarsInTreeWorkaround;
+
+ private Font fTreeFont;
+
// ------------------------------------------------------------------------
// Classes
// ------------------------------------------------------------------------
/**
- * The TreeContentProviderWrapper is used to insert filler items after
- * the elements of the tree's real content provider.
+ * The TimeGraphViewerExtension is used to set appropriate values and to
+ * override methods that could be called directly by the user and that must
+ * be handled by the time graph combo.
+ */
+ private class TimeGraphViewerExtension extends TimeGraphViewer {
+
+ private TimeGraphViewerExtension(Composite parent, int style, Tree tree) {
+ super(parent, style);
+ setItemHeight(TimeGraphCombo.this.getItemHeight(tree, true));
+ setHeaderHeight(tree.getHeaderHeight());
+ setBorderWidth(tree.getBorderWidth());
+ setNameWidthPref(0);
+ }
+
+ @Override
+ public ShowFilterDialogAction getShowFilterDialogAction() {
+ return TimeGraphCombo.this.getShowFilterDialogAction();
+ }
+
+ @Override
+ protected TimeGraphControl createTimeGraphControl(Composite composite, TimeGraphColorScheme colors) {
+ return new TimeGraphControl(composite, colors) {
+ @Override
+ public void verticalZoom(boolean zoomIn) {
+ TimeGraphCombo.this.verticalZoom(zoomIn);
+ }
+
+ @Override
+ public void resetVerticalZoom() {
+ TimeGraphCombo.this.resetVerticalZoom();
+ }
+
+ @Override
+ public void setElementPosition(ITimeGraphEntry entry, int y) {
+ /*
+ * Queue the update to make sure the time graph combo has
+ * finished updating the item heights.
+ */
+ getDisplay().asyncExec(() -> {
+ if (isDisposed()) {
+ return;
+ }
+ super.setElementPosition(entry, y);
+ alignTreeItems(false);
+ });
+ }
+ };
+ }
+
+ private class TimeGraphMarkerAxisExtension extends TimeGraphMarkerAxis {
+ private int fMargin = 0;
+
+ public TimeGraphMarkerAxisExtension(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+ super(parent, colorScheme, timeProvider);
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ Point size = super.computeSize(wHint, hHint, changed);
+ if (size.y > 0) {
+ size.y += fMargin;
+ }
+ return size;
+ }
+
+ @Override
+ public void redraw() {
+ super.redraw();
+ fTreeViewer.getControl().redraw();
+ }
+
+ @Override
+ protected void drawMarkerAxis(Rectangle bounds, int nameSpace, GC gc) {
+ super.drawMarkerAxis(bounds, nameSpace, gc);
+ }
+
+ private Rectangle getAxisBounds() {
+ Tree tree = fTreeViewer.getTree();
+ Rectangle axisBounds = getBounds();
+ Rectangle treeClientArea = tree.getClientArea();
+ if (axisBounds.isEmpty()) {
+ treeClientArea.y += treeClientArea.height;
+ treeClientArea.height = 0;
+ return treeClientArea;
+ }
+ Composite axisParent = getParent();
+ Point axisDisplayCoordinates = axisParent.toDisplay(axisBounds.x, axisBounds.y);
+ Point axisTreeCoordinates = tree.toControl(axisDisplayCoordinates);
+ int height = treeClientArea.y + treeClientArea.height - axisTreeCoordinates.y;
+ int margin = Math.max(0, axisBounds.height - height);
+ if (fMargin != margin) {
+ fMargin = margin;
+ getParent().layout();
+ redraw();
+ axisTreeCoordinates.y -= margin;
+ height += margin;
+ }
+ return new Rectangle(treeClientArea.x, axisTreeCoordinates.y, treeClientArea.width, height);
+ }
+ }
+
+ @Override
+ protected TimeGraphMarkerAxis createTimeGraphMarkerAxis(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+ TimeGraphMarkerAxisExtension timeGraphMarkerAxis = new TimeGraphMarkerAxisExtension(parent, colorScheme, timeProvider);
+ Tree tree = fTreeViewer.getTree();
+ tree.addPaintListener(e -> {
+ /*
+ * Draw the marker axis over the tree. Only the name area will
+ * be drawn, since the time area has zero width.
+ */
+ Rectangle bounds = timeGraphMarkerAxis.getAxisBounds();
+ e.gc.setBackground(timeGraphMarkerAxis.getBackground());
+ timeGraphMarkerAxis.drawMarkerAxis(bounds, bounds.width, e.gc);
+ });
+ tree.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent e) {
+ Rectangle bounds = timeGraphMarkerAxis.getAxisBounds();
+ if (bounds.contains(e.x, e.y)) {
+ timeGraphMarkerAxis.mouseDown(e, bounds, bounds.width);
+ }
+ }
+ });
+ tree.getHorizontalBar().addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ tree.redraw();
+ }
+ });
+ return timeGraphMarkerAxis;
+ }
+ }
+
+ /**
+ * The TreeContentProviderWrapper is used to insert filler items after the
+ * elements of the tree's real content provider.
*/
private class TreeContentProviderWrapper implements ITreeContentProvider {
private final ITreeContentProvider contentProvider;
}
/**
- * The TreeLabelProviderWrapper is used to intercept the filler items
- * from the calls to the tree's real label provider.
+ * The TreeLabelProviderWrapper is used to intercept the filler items from
+ * the calls to the tree's real label provider.
*/
- private class TreeLabelProviderWrapper implements ITableLabelProvider {
+ private static class TreeLabelProviderWrapper implements ITableLabelProvider {
private final ITableLabelProvider labelProvider;
public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {
}
/**
- * The ViewerFilterWrapper is used to intercept the filler items from
- * the time graph combo's real ViewerFilters. These filler items should
- * always be visible.
+ * The ViewerFilterWrapper is used to intercept the filler items from the
+ * time graph combo's real ViewerFilters. These filler items should always
+ * be visible.
*/
- private class ViewerFilterWrapper extends ViewerFilter {
+ private static class ViewerFilterWrapper extends ViewerFilter {
private ViewerFilter fWrappedFilter;
// ------------------------------------------------------------------------
/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
+ * Constructs a new instance of this class given its parent and a style
+ * value describing its behavior and appearance.
*
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
+ * @param parent
+ * a widget which will be the parent of the new instance (cannot
+ * be null)
+ * @param style
+ * the style of widget to construct
*/
public TimeGraphCombo(Composite parent, int style) {
this(parent, style, DEFAULT_WEIGHTS);
fSashForm = new SashForm(this, SWT.NONE);
- fTreeViewer = new TreeViewer(fSashForm, SWT.FULL_SELECTION | SWT.H_SCROLL);
+ /*
+ * In Windows, SWT.H_SCROLL | SWT.NO_SCROLL is not properly supported,
+ * both scroll bars are always created. See Tree.checkStyle: "Even when
+ * WS_HSCROLL or WS_VSCROLL is not specified, Windows creates trees and
+ * tables with scroll bars."
+ */
+ fScrollBarsInTreeWorkaround = "win32".equals(SWT.getPlatform()); //$NON-NLS-1$
+
+ int scrollBarStyle = fScrollBarsInTreeWorkaround ? SWT.H_SCROLL : SWT.H_SCROLL | SWT.NO_SCROLL;
+
+ fTreeViewer = new TreeViewer(fSashForm, SWT.FULL_SELECTION | scrollBarStyle);
fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
final Tree tree = fTreeViewer.getTree();
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
- fTimeGraphViewer = new TimeGraphViewer(fSashForm, SWT.NONE);
- fTimeGraphViewer.setItemHeight(getItemHeight(tree));
- fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
- fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());
- fTimeGraphViewer.setNameWidthPref(0);
+ fTimeGraphViewer = new TimeGraphViewerExtension(fSashForm, SWT.NONE, tree);
- // Feature in Windows. The tree vertical bar reappears when
- // the control is resized so we need to hide it again.
- tree.addControlListener(new ControlAdapter() {
- private int depth = 0;
- @Override
- public void controlResized(ControlEvent e) {
- if (depth == 0) {
- depth++;
- tree.getVerticalBar().setEnabled(false);
- // this can trigger controlResized recursively
- tree.getVerticalBar().setVisible(false);
- depth--;
+ if (fScrollBarsInTreeWorkaround) {
+ // Feature in Windows. The tree vertical bar reappears when
+ // the control is resized so we need to hide it again.
+ tree.addControlListener(new ControlAdapter() {
+ private int depth = 0;
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ if (depth == 0) {
+ depth++;
+ tree.getVerticalBar().setEnabled(false);
+ // this can trigger controlResized recursively
+ tree.getVerticalBar().setVisible(false);
+ depth--;
+ }
}
- }
- });
+ });
+ }
// Bug in Linux. The tree header height is 0 in constructor,
// so we need to reset it later when the control is painted.
// This work around used to be done on control resized but the header
// height was not initialized on the initial resize on GTK3.
tree.addPaintListener(new PaintListener() {
- @Override
+
+ @Override
public void paintControl(PaintEvent e) {
int headerHeight = tree.getHeaderHeight();
if (headerHeight > 0) {
}
});
+ tree.addDisposeListener(e -> {
+ if (fTreeFont != null) {
+ fTreeFont.dispose();
+ }
+ });
+
// ensure synchronization of expanded items between tree and time graph
fTreeViewer.addTreeListener(new ITreeViewerListener() {
@Override
});
// prevent mouse button from selecting a filler tree item
- tree.addListener(SWT.MouseDown, new Listener() {
- @Override
- public void handleEvent(Event event) {
- TreeItem treeItem = tree.getItem(new Point(event.x, event.y));
- if (treeItem == null || treeItem.getData() == FILLER) {
- event.doit = false;
- List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
- if (treeItems.size() == 0) {
- fTreeViewer.setSelection(new StructuredSelection());
- fTimeGraphViewer.setSelection(null);
- return;
- }
- // this prevents from scrolling up when selecting
- // the partially visible tree item at the bottom
- tree.select(treeItems.get(treeItems.size() - 1));
- fTreeViewer.setSelection(new StructuredSelection());
- fTimeGraphViewer.setSelection(null);
- }
+ tree.addListener(SWT.MouseDown, event -> {
+ List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
+ if (treeItems.isEmpty()) {
+ event.doit = false;
+ fTreeViewer.setSelection(new StructuredSelection());
+ fTimeGraphViewer.setSelection(null);
+ return;
+ }
+ TreeItem lastTreeItem = treeItems.get(treeItems.size() - 1);
+ if (event.y >= lastTreeItem.getBounds().y + lastTreeItem.getBounds().height) {
+ event.doit = false;
+ // this prevents from scrolling up when selecting
+ // the partially visible tree item at the bottom
+ tree.select(treeItems.get(treeItems.size() - 1));
+ fTreeViewer.setSelection(new StructuredSelection());
+ fTimeGraphViewer.setSelection(null);
}
});
// prevent mouse wheel from scrolling down into filler tree items
- tree.addListener(SWT.MouseWheel, new Listener() {
- @Override
- public void handleEvent(Event event) {
- event.doit = false;
- Slider scrollBar = fTimeGraphViewer.getVerticalBar();
- fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
- alignTreeItems(false);
+ tree.addListener(SWT.MouseWheel, event -> {
+ event.doit = false;
+ if (event.count == 0) {
+ return;
}
+ Slider scrollBar = fTimeGraphViewer.getVerticalBar();
+ fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
+ alignTreeItems(false);
});
// prevent key stroke from selecting a filler tree item
- tree.addListener(SWT.KeyDown, new Listener() {
- @Override
- public void handleEvent(Event event) {
- List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
- if (treeItems.size() == 0) {
- fTreeViewer.setSelection(new StructuredSelection());
- event.doit = false;
- return;
- }
- if (event.keyCode == SWT.ARROW_DOWN) {
- int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
- fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
- event.doit = false;
- } else if (event.keyCode == SWT.PAGE_DOWN) {
- int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
- int countPerPage = height / getItemHeight(tree);
- int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
- fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
- event.doit = false;
- } else if (event.keyCode == SWT.END) {
- fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
- event.doit = false;
- }
- if (fTimeGraphViewer.getSelectionIndex() >= 0) {
- fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
- } else {
- fTreeViewer.setSelection(new StructuredSelection());
- }
- alignTreeItems(false);
+ tree.addListener(SWT.KeyDown, event -> {
+ List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
+ if (treeItems.size() == 0) {
+ fTreeViewer.setSelection(new StructuredSelection());
+ event.doit = false;
+ return;
+ }
+ if (event.keyCode == SWT.ARROW_DOWN) {
+ int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
+ event.doit = false;
+ } else if (event.keyCode == SWT.PAGE_DOWN) {
+ int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
+ int countPerPage = height / getItemHeight(tree, false);
+ int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
+ event.doit = false;
+ } else if (event.keyCode == SWT.END) {
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
+ event.doit = false;
+ } else if ((event.character == '+' || event.character == '=') && ((event.stateMask & SWT.CTRL) != 0)) {
+ fTimeGraphViewer.getTimeGraphControl().keyPressed(new KeyEvent(event));
+ return;
+ } else if (event.character == '-' && ((event.stateMask & SWT.CTRL) != 0)) {
+ fTimeGraphViewer.getTimeGraphControl().keyPressed(new KeyEvent(event));
+ return;
+ } else if (event.character == '0' && ((event.stateMask & SWT.CTRL) != 0)) {
+ fTimeGraphViewer.getTimeGraphControl().keyPressed(new KeyEvent(event));
+ return;
+ } else {
+ return;
+ }
+ if (fTimeGraphViewer.getSelectionIndex() >= 0) {
+ fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
+ } else {
+ fTreeViewer.setSelection(new StructuredSelection());
}
+ alignTreeItems(false);
});
// ensure alignment of top item between tree and time graph
});
// ensure synchronization of selected item between tree and time graph
- fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
- @Override
- public void selectionChanged(SelectionChangedEvent event) {
- if (fInhibitTreeSelection) {
- return;
- }
- if (event.getSelection() instanceof IStructuredSelection) {
- Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
- if (selection instanceof ITimeGraphEntry) {
- fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
- }
- alignTreeItems(false);
+ fTreeViewer.addSelectionChangedListener(event -> {
+ if (fInhibitTreeSelection) {
+ return;
+ }
+ if (event.getSelection() instanceof IStructuredSelection) {
+ Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
+ if (selection instanceof ITimeGraphEntry) {
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
}
+ alignTreeItems(false);
}
});
// ensure synchronization of selected item between tree and time graph
- fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {
- @Override
- public void selectionChanged(TimeGraphSelectionEvent event) {
- ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
- fInhibitTreeSelection = true; // block the tree selection changed listener
- if (entry != null) {
- StructuredSelection selection = new StructuredSelection(entry);
- fTreeViewer.setSelection(selection);
- } else {
- fTreeViewer.setSelection(new StructuredSelection());
- }
- fInhibitTreeSelection = false;
- alignTreeItems(false);
- }
+ fTimeGraphViewer.addSelectionListener(event -> {
+ ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
+ setSelectionInTree(entry);
});
// ensure alignment of top item between tree and time graph
fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
- @Override
+
+ @Override
public void widgetSelected(SelectionEvent e) {
alignTreeItems(false);
}
});
// ensure alignment of top item between tree and time graph
- fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
- @Override
- public void mouseScrolled(MouseEvent e) {
- alignTreeItems(false);
+ fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(e -> {
+ if (e.count == 0) {
+ return;
}
+ alignTreeItems(false);
});
// ensure the tree has focus control when mouse is over it if the time graph had control
// The filler rows are required to ensure alignment when the tree does not have a
// visible horizontal scroll bar. The tree does not allow its top item to be set
// to a value that would cause blank space to be drawn at the bottom of the tree.
- fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);
+ fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree, false);
fSashForm.setWeights(weights);
});
}
+ private void verticalZoom(boolean zoomIn) {
+ Tree tree = fTreeViewer.getTree();
+ FontData fontData = tree.getFont().getFontData()[0];
+ int height = fontData.getHeight() + (zoomIn ? 1 : -1);
+ if (height <= 0) {
+ return;
+ }
+ fontData.setHeight(height);
+ if (fTreeFont != null) {
+ fTreeFont.dispose();
+ }
+ fTreeFont = new Font(tree.getDisplay(), fontData);
+ tree.setFont(fTreeFont);
+ redraw();
+ update();
+ fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
+ fTimeGraphViewer.setItemHeight(getItemHeight(tree, true));
+ alignTreeItems(false);
+ }
+
+ private void resetVerticalZoom() {
+ Tree tree = fTreeViewer.getTree();
+ if (fTreeFont != null) {
+ fTreeFont.dispose();
+ fTreeFont = null;
+ }
+ tree.setFont(null);
+ redraw();
+ update();
+ fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
+ fTimeGraphViewer.setItemHeight(getItemHeight(tree, true));
+ alignTreeItems(false);
+ }
+
private void sendTimeViewAlignmentChanged() {
TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm, getTimeViewAlignmentInfo()));
}
* Get the show filter dialog action.
*
* @return The Action object
- * @since 2.0
+ * @since 1.2
*/
public ShowFilterDialogAction getShowFilterDialogAction() {
if (fShowFilterDialogAction == null) {
super.redraw();
}
+ @Override
+ public void update() {
+ fTimeGraphViewer.getControl().update();
+ super.update();
+ }
+
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
/**
* Sets the tree content provider used by this time graph combo.
*
- * @param contentProvider the tree content provider
+ * @param contentProvider
+ * the tree content provider
*/
public void setTreeContentProvider(ITreeContentProvider contentProvider) {
fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
/**
* Sets the tree label provider used by this time graph combo.
*
- * @param labelProvider the tree label provider
+ * @param labelProvider
+ * the tree label provider
*/
public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
/**
* Sets the tree content provider used by the filter dialog
*
- * @param contentProvider the tree content provider
+ * @param contentProvider
+ * the tree content provider
*/
public void setFilterContentProvider(ITreeContentProvider contentProvider) {
getShowFilterDialogAction().getFilterDialog().setContentProvider(contentProvider);
/**
* Sets the tree label provider used by the filter dialog
*
- * @param labelProvider the tree label provider
+ * @param labelProvider
+ * the tree label provider
*/
public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
getShowFilterDialogAction().getFilterDialog().setLabelProvider(labelProvider);
/**
* Sets the tree columns for this time graph combo.
*
- * @param columnNames the tree column names
+ * @param columnNames
+ * the tree column names
*/
public void setTreeColumns(String[] columnNames) {
final Tree tree = fTreeViewer.getTree();
for (String columnName : columnNames) {
TreeColumn column = new TreeColumn(tree, SWT.LEFT);
+ column.setMoveable(true);
column.setText(columnName);
column.pack();
}
/**
* Sets the tree columns for this time graph combo's filter dialog.
*
- * @param columnNames the tree column names
+ * @param columnNames
+ * the tree column names
*/
public void setFilterColumns(String[] columnNames) {
getShowFilterDialogAction().getFilterDialog().setColumnNames(columnNames);
/**
* Sets the time graph presentation provider used by this time graph combo.
*
- * @param timeGraphProvider the time graph provider
+ * @param timeGraphProvider
+ * the time graph provider
*/
public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
/**
* Sets or clears the input for this time graph combo.
*
- * @param input the input of this time graph combo, or <code>null</code> if none
+ * @param input
+ * the input of this time graph combo, or <code>null</code> if
+ * none
*/
public void setInput(Object input) {
fInhibitTreeSelection = true;
listenerWrapper.selection = null;
}
fInhibitTreeSelection = false;
- fTreeViewer.getTree().getVerticalBar().setEnabled(false);
- fTreeViewer.getTree().getVerticalBar().setVisible(false);
+ if (fScrollBarsInTreeWorkaround) {
+ fTreeViewer.getTree().getVerticalBar().setEnabled(false);
+ fTreeViewer.getTree().getVerticalBar().setVisible(false);
+ }
fTimeGraphViewer.setInput(input);
- fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));
+ fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree(), false));
// queue the alignment update because in Linux the item bounds are not
// set properly until the tree has been painted at least once
fVisibleExpandedItems = null; // invalidate the cache
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
+ if (isDisposed()) {
+ return;
+ }
alignTreeItems(true);
- }});
+ }
+ });
}
/**
/**
* Sets or clears the list of links to display on this combo
*
- * @param links the links to display in this time graph combo
+ * @param links
+ * the links to display in this time graph combo
*/
public void setLinks(List<ILinkEvent> links) {
fTimeGraphViewer.setLinks(links);
}
/**
- * @param filter The filter object to be attached to the view
+ * @param filter
+ * The filter object to be attached to the view
*/
- public void addFilter(ViewerFilter filter) {
+ public void addFilter(@NonNull ViewerFilter filter) {
fInhibitTreeSelection = true;
ViewerFilter wrapper = new ViewerFilterWrapper(filter);
fTreeViewer.addFilter(wrapper);
}
/**
- * @param filter The filter object to be removed from the view
+ * @param filter
+ * The filter object to be removed from the view
*/
- public void removeFilter(ViewerFilter filter) {
+ public void removeFilter(@NonNull ViewerFilter filter) {
fInhibitTreeSelection = true;
ViewerFilter wrapper = fViewerFilterMap.get(filter);
fTreeViewer.removeFilter(wrapper);
* Returns this viewer's filters.
*
* @return an array of viewer filters
- * @since 2.0
+ * @since 1.2
*/
- public ViewerFilter[] getFilters() {
+ public @NonNull ViewerFilter[] getFilters() {
return fTimeGraphViewer.getFilters();
}
*
* @param filters
* an array of viewer filters, or null
- * @since 2.0
+ * @since 1.2
*/
- public void setFilters(ViewerFilter[] filters) {
+ public void setFilters(@NonNull ViewerFilter[] filters) {
fInhibitTreeSelection = true;
fViewerFilterMap.clear();
if (filters == null) {
}
/**
- * Refreshes this time graph completely with information freshly obtained from its model.
+ * Refreshes this time graph completely with information freshly obtained
+ * from its model.
*/
public void refresh() {
fInhibitTreeSelection = true;
/**
* Adds a listener for selection changes in this time graph combo.
*
- * @param listener a selection listener
+ * @param listener
+ * a selection listener
*/
public void addSelectionListener(ITimeGraphSelectionListener listener) {
SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
/**
* Removes the given selection listener from this time graph combo.
*
- * @param listener a selection changed listener
+ * @param listener
+ * a selection changed listener
*/
public void removeSelectionListener(ITimeGraphSelectionListener listener) {
SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
/**
* Sets the current selection for this time graph combo.
*
- * @param selection the new selection
+ * @param selection
+ * the new selection
*/
public void setSelection(ITimeGraphEntry selection) {
fTimeGraphViewer.setSelection(selection);
- fInhibitTreeSelection = true; // block the tree selection changed listener
+ setSelectionInTree(selection);
+ }
+
+ /**
+ * Sets the current selection for this time graph combo and reveal it if
+ * needed.
+ *
+ * @param selection
+ * The new selection
+ * @since 2.0
+ */
+ public void selectAndReveal(@NonNull ITimeGraphEntry selection) {
+ fTimeGraphViewer.selectAndReveal(selection);
+ setSelectionInTree(selection);
+ }
+
+ /**
+ * Select the entry in the tree structure
+ *
+ * @param selection
+ * The new selection
+ */
+ private void setSelectionInTree(ITimeGraphEntry selection) {
+ fInhibitTreeSelection = true; // block the tree selection changed
+ // listener
if (selection != null) {
StructuredSelection structuredSelection = new StructuredSelection(selection);
fTreeViewer.setSelection(structuredSelection);
return fTimeGraphViewer.getAutoExpandLevel();
}
+ /**
+ * Get the expanded state of an entry.
+ *
+ * @param entry
+ * The entry
+ * @return true if the entry is expanded, false if collapsed
+ * @since 2.0
+ */
+ public boolean getExpandedState(ITimeGraphEntry entry) {
+ return fTimeGraphViewer.getExpandedState(entry);
+ }
+
/**
* Set the expanded state of an entry
*
}
}
- private int getItemHeight(final Tree tree) {
+ private int getItemHeight(final Tree tree, boolean force) {
/*
- * Bug in Linux. The method getItemHeight doesn't always return the correct value.
+ * Bug in Linux. The method getItemHeight doesn't always return the
+ * correct value.
*/
if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
- if (fLinuxItemHeight != 0) {
+ if (fLinuxItemHeight != 0 && !force) {
return fLinuxItemHeight;
}
tree.addPaintListener(paintListener);
}
} else {
- fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
+ fLinuxItemHeight = -1; // Not Linux, don't perform os.name check
+ // anymore
}
return tree.getItemHeight();
}
private void alignTreeItems(boolean refreshExpandedItems) {
+
// align the tree top item with the time graph top item
Tree tree = fTreeViewer.getTree();
List<TreeItem> treeItems = getVisibleExpandedItems(tree, refreshExpandedItems);
}
TreeItem item = treeItems.get(topIndex);
tree.setTopItem(item);
+ /*
+ * In GTK3, the bounds of the tree items are only sure to be correct
+ * after the tree has been painted.
+ */
+ tree.addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ tree.removePaintListener(this);
+ doAlignTreeItems();
+ redraw();
+ }
+ });
+ /* Make sure the paint event is triggered. */
+ tree.redraw();
+ }
+
+ private void doAlignTreeItems() {
+ Tree tree = fTreeViewer.getTree();
+ List<TreeItem> treeItems = getVisibleExpandedItems(tree, false);
+ int topIndex = fTimeGraphViewer.getTopIndex();
+ if (topIndex >= treeItems.size()) {
+ return;
+ }
+ TreeItem item = treeItems.get(topIndex);
+
+ // get the first filler item so we can calculate the last item's height
+ TreeItem fillerItem = null;
+ for (TreeItem treeItem : fTreeViewer.getTree().getItems()) {
+ if (treeItem.getData() == FILLER) {
+ fillerItem = treeItem;
+ break;
+ }
+ }
// ensure the time graph item heights are equal to the tree item heights
int treeHeight = fTreeViewer.getTree().getBounds().height;
int index = topIndex;
Rectangle bounds = item.getBounds();
- while (index < treeItems.size() - 1) {
+ while (index < treeItems.size()) {
if (bounds.y > treeHeight) {
break;
}
- /*
- * Bug in Linux. The method getBounds doesn't always return the correct height.
- * Use the difference of y position between items to calculate the height.
- */
- TreeItem nextItem = treeItems.get(index + 1);
- Rectangle nextBounds = nextItem.getBounds();
- Integer itemHeight = nextBounds.y - bounds.y;
- if (itemHeight > 0) {
- ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
- fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight);
- }
+ TreeItem nextItem = (index + 1 == treeItems.size()) ? fillerItem : treeItems.get(index + 1);
+ Rectangle nextBounds = alignTreeItem(item, bounds, nextItem);
index++;
item = nextItem;
bounds = nextBounds;
}
+
+ /*
+ * When an item's height in the time graph changes, it is possible that
+ * the time graph readjusts its top index to fill empty space at the
+ * bottom of the viewer. Calling method setTopIndex() triggers this
+ * adjustment, if needed. In that case, we need to make sure that the
+ * newly visible items at the top of the viewer are also aligned.
+ */
+ fTimeGraphViewer.setTopIndex(topIndex);
+ if (fTimeGraphViewer.getTopIndex() != topIndex) {
+ alignTreeItems(false);
+ }
+ }
+
+ private Rectangle alignTreeItem(TreeItem item, Rectangle bounds, TreeItem nextItem) {
+ /*
+ * Bug in Linux. The method getBounds doesn't always return the correct
+ * height. Use the difference of y position between items to calculate
+ * the height.
+ */
+ Rectangle nextBounds = nextItem.getBounds();
+ Integer itemHeight = nextBounds.y - bounds.y;
+ if (itemHeight > 0) {
+ ITimeGraphEntry entry = (ITimeGraphEntry) item.getData();
+ fTimeGraphViewer.getTimeGraphControl().setItemHeight(entry, itemHeight);
+ }
+ return nextBounds;
}
/**
int timeAxisOffset = Math.min(offset, total);
int width1 = Math.max(0, timeAxisOffset - fSashForm.getSashWidth());
int width2 = total - timeAxisOffset;
- fSashForm.setWeights(new int[] { width1, width2 });
- fSashForm.layout();
+ if (width1 >= 0 && width2 > 0 || width1 > 0 && width2 >= 0) {
+ fSashForm.setWeights(new int[] { width1, width2 });
+ fSashForm.layout();
+ }
Composite composite = fTimeGraphViewer.getTimeAlignedComposite();
GridLayout layout = (GridLayout) composite.getLayout();