From 837a2f8c18b131986ae762068f151086614af1a2 Mon Sep 17 00:00:00 2001 From: Patrick Tasse Date: Wed, 5 Sep 2012 13:49:20 -0400 Subject: [PATCH] Add tree handling APIs and fix tree synchronization in TimeGraphCombo. Change-Id: Ic3f871a72eea0cdf97de146634075a42dd47f550 Reviewed-on: https://git.eclipse.org/r/7624 Tested-by: Hudson CI Reviewed-by: Bernd Hufmann IP-Clean: Bernd Hufmann --- .../ui/widgets/timegraph/TimeGraphCombo.java | 1502 +++---- .../ui/widgets/timegraph/TimeGraphViewer.java | 2700 ++++++------ .../timegraph/widgets/TimeGraphControl.java | 3808 +++++++++-------- 3 files changed, 4055 insertions(+), 3955 deletions(-) diff --git a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphCombo.java b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphCombo.java index f8039cd4c0..023f47b345 100644 --- a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphCombo.java +++ b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphCombo.java @@ -1,724 +1,778 @@ -/******************************************************************************* - * Copyright (c) 2012 Ericsson - * - * 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 - * - * Contributors: - * Patrick Tasse - Initial API and implementation - *******************************************************************************/ - -package org.eclipse.linuxtools.tmf.ui.widgets.timegraph; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; - -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.ITreeViewerListener; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TreeExpansionEvent; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -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.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Slider; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; -import org.eclipse.swt.widgets.TreeItem; - -/** - * Time graph "combo" view (with the list/tree on the left and the gantt chart - * on the right) - * - * @version 1.0 - * @author Patrick Tasse - */ -public class TimeGraphCombo extends Composite { - - // ------------------------------------------------------------------------ - // Constants - // ------------------------------------------------------------------------ - - private static final Object FILLER = new Object(); - - // ------------------------------------------------------------------------ - // Fields - // ------------------------------------------------------------------------ - - // The tree viewer - private TreeViewer fTreeViewer; - - // The time viewer - private TimeGraphViewer fTimeGraphViewer; - - // The selection listener map - private final HashMap fSelectionListenerMap = new HashMap(); - - // Flag to block the tree selection changed listener when triggered by the time graph combo - private boolean fInhibitTreeSelection = false; - - // Number of filler rows used by the tree content provider - private int fNumFillerRows; - - // Calculated item height for Linux workaround - private int fLinuxItemHeight = 0; - - // ------------------------------------------------------------------------ - // Classes - // ------------------------------------------------------------------------ - - /** - * 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; - - public TreeContentProviderWrapper(ITreeContentProvider contentProvider) { - this.contentProvider = contentProvider; - } - - @Override - public void dispose() { - contentProvider.dispose(); - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - contentProvider.inputChanged(viewer, oldInput, newInput); - } - - @Override - public Object[] getElements(Object inputElement) { - Object[] elements = contentProvider.getElements(inputElement); - // add filler elements to ensure alignment with time analysis viewer - Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass()); - for (int i = 0; i < fNumFillerRows; i++) { - oElements[elements.length + i] = FILLER; - } - return oElements; - } - - @Override - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof ITimeGraphEntry) { - return contentProvider.getChildren(parentElement); - } - return new Object[0]; - } - - @Override - public Object getParent(Object element) { - if (element instanceof ITimeGraphEntry) { - return contentProvider.getParent(element); - } - return null; - } - - @Override - public boolean hasChildren(Object element) { - if (element instanceof ITimeGraphEntry) { - return contentProvider.hasChildren(element); - } - return false; - } - } - - /** - * 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 final ITableLabelProvider labelProvider; - - public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) { - this.labelProvider = labelProvider; - } - - @Override - public void addListener(ILabelProviderListener listener) { - labelProvider.addListener(listener); - } - - @Override - public void dispose() { - labelProvider.dispose(); - } - - @Override - public boolean isLabelProperty(Object element, String property) { - if (element instanceof ITimeGraphEntry) { - return labelProvider.isLabelProperty(element, property); - } - return false; - } - - @Override - public void removeListener(ILabelProviderListener listener) { - labelProvider.removeListener(listener); - } - - @Override - public Image getColumnImage(Object element, int columnIndex) { - if (element instanceof ITimeGraphEntry) { - return labelProvider.getColumnImage(element, columnIndex); - } - return null; - } - - @Override - public String getColumnText(Object element, int columnIndex) { - if (element instanceof ITimeGraphEntry) { - return labelProvider.getColumnText(element, columnIndex); - } - return null; - } - - } - - /** - * The SelectionListenerWrapper is used to intercept the filler items from - * the time graph combo's real selection listener, and to prevent double - * notifications from being sent when selection changes in both tree and - * time graph at the same time. - */ - private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener { - private final ITimeGraphSelectionListener listener; - private ITimeGraphEntry selection = null; - - public SelectionListenerWrapper(ITimeGraphSelectionListener listener) { - this.listener = listener; - } - - @Override - public void selectionChanged(SelectionChangedEvent event) { - if (fInhibitTreeSelection) { - return; - } - Object element = ((IStructuredSelection) event.getSelection()).getFirstElement(); - if (element instanceof ITimeGraphEntry) { - ITimeGraphEntry entry = (ITimeGraphEntry) element; - if (entry != selection) { - selection = entry; - listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection)); - } - } - } - - @Override - public void selectionChanged(TimeGraphSelectionEvent event) { - ITimeGraphEntry entry = event.getSelection(); - if (entry != selection) { - selection = entry; - listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection)); - } - } - } - - // ------------------------------------------------------------------------ - // Constructors - // ------------------------------------------------------------------------ - - /** - * 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 - */ - public TimeGraphCombo(Composite parent, int style) { - super(parent, style); - setLayout(new FillLayout()); - - final SashForm sash = new SashForm(this, SWT.NONE); - - fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL); - final Tree tree = fTreeViewer.getTree(); - tree.setHeaderVisible(true); - tree.setLinesVisible(true); - - fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE); - fTimeGraphViewer.setItemHeight(getItemHeight(tree)); - fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight()); - fTimeGraphViewer.setBorderWidth(tree.getBorderWidth()); - fTimeGraphViewer.setNameWidthPref(0); - - // Feature in Windows. The tree vertical bar reappears when - // the control is resized so we need to hide it again. - // Bug in Linux. The tree header height is 0 in constructor, - // so we need to reset it later when the control is resized. - tree.addControlListener(new ControlAdapter() { - 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--; - } - fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight()); - } - }); - - // ensure synchronization of expanded items between tree and time graph - fTreeViewer.addTreeListener(new ITreeViewerListener() { - @Override - public void treeCollapsed(TreeExpansionEvent event) { - fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false); - } - - @Override - public void treeExpanded(TreeExpansionEvent event) { - fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true); - } - }); - - // ensure synchronization of expanded items between tree and time graph - fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() { - @Override - public void treeCollapsed(TimeGraphTreeExpansionEvent event) { - fTreeViewer.setExpandedState(event.getEntry(), false); - } - - @Override - public void treeExpanded(TimeGraphTreeExpansionEvent event) { - fTreeViewer.setExpandedState(event.getEntry(), true); - } - }); - - // 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; - ArrayList treeItems = getVisibleExpandedItems(tree); - 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); - } - } - }); - - // 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); - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - } - }); - - // prevent key stroke from selecting a filler tree item - tree.addListener(SWT.KeyDown, new Listener() { - @Override - public void handleEvent(Event event) { - ArrayList treeItems = getVisibleExpandedItems(tree); - 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; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - if (fTimeGraphViewer.getSelectionIndex() >= 0) { - fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection())); - } else { - fTreeViewer.setSelection(new StructuredSelection()); - } - } - }); - - // ensure alignment of top item between tree and time graph - fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() { - @Override - public void controlResized(ControlEvent e) { - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - } - }); - - // 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); - } - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - } - } - }); - - // 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; - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - } - }); - - // ensure alignment of top item between tree and time graph - fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - } - }); - - // ensure alignment of top item between tree and time graph - fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() { - @Override - public void mouseScrolled(MouseEvent e) { - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - tree.setTopItem(treeItem); - } - }); - - // ensure the tree has focus control when mouse is over it if the time graph had control - fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() { - @Override - public void mouseEnter(MouseEvent e) { - if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) { - fTreeViewer.getControl().setFocus(); - } - } - }); - - // ensure the time graph has focus control when mouse is over it if the tree had control - fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() { - @Override - public void mouseEnter(MouseEvent e) { - if (fTreeViewer.getControl().isFocusControl()) { - fTimeGraphViewer.getTimeGraphControl().setFocus(); - } - } - }); - fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() { - @Override - public void mouseEnter(MouseEvent e) { - if (fTreeViewer.getControl().isFocusControl()) { - fTimeGraphViewer.getTimeGraphControl().setFocus(); - } - } - }); - - // 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); - - sash.setWeights(new int[] { 1, 1 }); - } - - // ------------------------------------------------------------------------ - // Accessors - // ------------------------------------------------------------------------ - - /** - * Returns this time graph combo's tree viewer. - * - * @return the tree viewer - */ - public TreeViewer getTreeViewer() { - return fTreeViewer; - } - - /** - * Returns this time graph combo's time graph viewer. - * - * @return the time graph viewer - */ - public TimeGraphViewer getTimeGraphViewer() { - return fTimeGraphViewer; - } - - // ------------------------------------------------------------------------ - // Control - // ------------------------------------------------------------------------ - - /* (non-Javadoc) - * @see org.eclipse.swt.widgets.Control#redraw() - */ - @Override - public void redraw() { - fTimeGraphViewer.getControl().redraw(); - super.redraw(); - } - - // ------------------------------------------------------------------------ - // Operations - // ------------------------------------------------------------------------ - - /** - * Sets the tree content provider used by this time graph combo. - * - * @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 - */ - public void setTreeLabelProvider(ITableLabelProvider labelProvider) { - fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider)); - } - - /** - * Sets the tree columns for this time graph combo. - * - * @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.setText(columnName); - column.pack(); - } - } - - - /** - * Sets the time graph provider used by this time graph combo. - * - * @param timeGraphProvider the time graph provider - */ - public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { - fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider); - } - - /** - * Sets or clears the input for this time graph combo. - * The input array should only contain top-level elements. - * - * @param input the input of this time graph combo, or null if none - */ - public void setInput(ITimeGraphEntry[] input) { - fInhibitTreeSelection = true; - fTreeViewer.setInput(input); - for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) { - listenerWrapper.selection = null; - } - fInhibitTreeSelection = false; - fTreeViewer.expandAll(); - fTreeViewer.getTree().getVerticalBar().setEnabled(false); - fTreeViewer.getTree().getVerticalBar().setVisible(false); - fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree())); - fTimeGraphViewer.setInput(input); - } - - /** - * Refreshes this time graph completely with information freshly obtained from its model. - */ - public void refresh() { - fTreeViewer.refresh(); - fTimeGraphViewer.refresh(); - } - - /** - * Adds a listener for selection changes in this time graph combo. - * - * @param listener a selection listener - */ - public void addSelectionListener(ITimeGraphSelectionListener listener) { - SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener); - fTreeViewer.addSelectionChangedListener(listenerWrapper); - fSelectionListenerMap.put(listener, listenerWrapper); - fTimeGraphViewer.addSelectionListener(listenerWrapper); - } - - /** - * Removes the given selection listener from this time graph combo. - * - * @param listener a selection changed listener - */ - public void removeSelectionListener(ITimeGraphSelectionListener listener) { - SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener); - fTreeViewer.removeSelectionChangedListener(listenerWrapper); - fTimeGraphViewer.removeSelectionListener(listenerWrapper); - } - - /** - * Sets the current selection for this time graph combo. - * - * @param selection the new selection - */ - public void setSelection(ITimeGraphEntry selection) { - fTimeGraphViewer.setSelection(selection); - fInhibitTreeSelection = true; // block the tree selection changed listener - if (selection != null) { - StructuredSelection structuredSelection = new StructuredSelection(selection); - fTreeViewer.setSelection(structuredSelection); - } else { - fTreeViewer.setSelection(new StructuredSelection()); - } - fInhibitTreeSelection = false; - ArrayList treeItems = getVisibleExpandedItems(fTreeViewer.getTree()); - if (treeItems.size() == 0) { - return; - } - TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); - fTreeViewer.getTree().setTopItem(treeItem); - } - - // ------------------------------------------------------------------------ - // Internal - // ------------------------------------------------------------------------ - - private ArrayList getVisibleExpandedItems(Tree tree) { - ArrayList items = new ArrayList(); - for (TreeItem item : tree.getItems()) { - if (item.getData() == FILLER) { - break; - } - items.add(item); - if (item.getExpanded()) { - items.addAll(getVisibleExpandedItems(item)); - } - } - return items; - } - - private ArrayList getVisibleExpandedItems(TreeItem treeItem) { - ArrayList items = new ArrayList(); - for (TreeItem item : treeItem.getItems()) { - items.add(item); - if (item.getExpanded()) { - items.addAll(getVisibleExpandedItems(item)); - } - } - return items; - } - - private int getItemHeight(final Tree tree) { - /* - * 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) { - return fLinuxItemHeight; - } - ArrayList treeItems = getVisibleExpandedItems(tree); - if (treeItems.size() > 1) { - final TreeItem treeItem0 = treeItems.get(0); - final TreeItem treeItem1 = treeItems.get(1); - PaintListener paintListener = new PaintListener() { - @Override - public void paintControl(PaintEvent e) { - tree.removePaintListener(this); - int y0 = treeItem0.getBounds().y; - int y1 = treeItem1.getBounds().y; - int itemHeight = y1 - y0; - if (itemHeight > 0) { - fLinuxItemHeight = itemHeight; - fTimeGraphViewer.setItemHeight(itemHeight); - } - } - }; - tree.addPaintListener(paintListener); - } - } else { - fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore - } - return tree.getItemHeight(); - } - -} +/******************************************************************************* + * Copyright (c) 2012 Ericsson + * + * 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 + * + * Contributors: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.tmf.ui.widgets.timegraph; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.ITreeViewerListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeExpansionEvent; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +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.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Slider; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; + +/** + * Time graph "combo" view (with the list/tree on the left and the gantt chart + * on the right) + * + * @version 1.0 + * @author Patrick Tasse + */ +public class TimeGraphCombo extends Composite { + + // ------------------------------------------------------------------------ + // Constants + // ------------------------------------------------------------------------ + + private static final Object FILLER = new Object(); + + // ------------------------------------------------------------------------ + // Fields + // ------------------------------------------------------------------------ + + // The tree viewer + private TreeViewer fTreeViewer; + + // The time viewer + private TimeGraphViewer fTimeGraphViewer; + + // The selection listener map + private final HashMap fSelectionListenerMap = new HashMap(); + + // Flag to block the tree selection changed listener when triggered by the time graph combo + private boolean fInhibitTreeSelection = false; + + // Number of filler rows used by the tree content provider + private int fNumFillerRows; + + // Calculated item height for Linux workaround + private int fLinuxItemHeight = 0; + + // ------------------------------------------------------------------------ + // Classes + // ------------------------------------------------------------------------ + + /** + * 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; + + public TreeContentProviderWrapper(ITreeContentProvider contentProvider) { + this.contentProvider = contentProvider; + } + + @Override + public void dispose() { + contentProvider.dispose(); + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + contentProvider.inputChanged(viewer, oldInput, newInput); + } + + @Override + public Object[] getElements(Object inputElement) { + Object[] elements = contentProvider.getElements(inputElement); + // add filler elements to ensure alignment with time analysis viewer + Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass()); + for (int i = 0; i < fNumFillerRows; i++) { + oElements[elements.length + i] = FILLER; + } + return oElements; + } + + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof ITimeGraphEntry) { + return contentProvider.getChildren(parentElement); + } + return new Object[0]; + } + + @Override + public Object getParent(Object element) { + if (element instanceof ITimeGraphEntry) { + return contentProvider.getParent(element); + } + return null; + } + + @Override + public boolean hasChildren(Object element) { + if (element instanceof ITimeGraphEntry) { + return contentProvider.hasChildren(element); + } + return false; + } + } + + /** + * 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 final ITableLabelProvider labelProvider; + + public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) { + this.labelProvider = labelProvider; + } + + @Override + public void addListener(ILabelProviderListener listener) { + labelProvider.addListener(listener); + } + + @Override + public void dispose() { + labelProvider.dispose(); + } + + @Override + public boolean isLabelProperty(Object element, String property) { + if (element instanceof ITimeGraphEntry) { + return labelProvider.isLabelProperty(element, property); + } + return false; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + labelProvider.removeListener(listener); + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + if (element instanceof ITimeGraphEntry) { + return labelProvider.getColumnImage(element, columnIndex); + } + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + if (element instanceof ITimeGraphEntry) { + return labelProvider.getColumnText(element, columnIndex); + } + return null; + } + + } + + /** + * The SelectionListenerWrapper is used to intercept the filler items from + * the time graph combo's real selection listener, and to prevent double + * notifications from being sent when selection changes in both tree and + * time graph at the same time. + */ + private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener { + private final ITimeGraphSelectionListener listener; + private ITimeGraphEntry selection = null; + + public SelectionListenerWrapper(ITimeGraphSelectionListener listener) { + this.listener = listener; + } + + @Override + public void selectionChanged(SelectionChangedEvent event) { + if (fInhibitTreeSelection) { + return; + } + Object element = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (element instanceof ITimeGraphEntry) { + ITimeGraphEntry entry = (ITimeGraphEntry) element; + if (entry != selection) { + selection = entry; + listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection)); + } + } + } + + @Override + public void selectionChanged(TimeGraphSelectionEvent event) { + ITimeGraphEntry entry = event.getSelection(); + if (entry != selection) { + selection = entry; + listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection)); + } + } + } + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * 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 + */ + public TimeGraphCombo(Composite parent, int style) { + super(parent, style); + setLayout(new FillLayout()); + + final SashForm sash = new SashForm(this, SWT.NONE); + + fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL); + final Tree tree = fTreeViewer.getTree(); + tree.setHeaderVisible(true); + tree.setLinesVisible(true); + + fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE); + fTimeGraphViewer.setItemHeight(getItemHeight(tree)); + fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight()); + fTimeGraphViewer.setBorderWidth(tree.getBorderWidth()); + fTimeGraphViewer.setNameWidthPref(0); + + // Feature in Windows. The tree vertical bar reappears when + // the control is resized so we need to hide it again. + // Bug in Linux. The tree header height is 0 in constructor, + // so we need to reset it later when the control is resized. + tree.addControlListener(new ControlAdapter() { + 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--; + } + fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight()); + } + }); + + // ensure synchronization of expanded items between tree and time graph + fTreeViewer.addTreeListener(new ITreeViewerListener() { + @Override + public void treeCollapsed(TreeExpansionEvent event) { + fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false); + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + + @Override + public void treeExpanded(TreeExpansionEvent event) { + fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true); + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + final TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + // queue the top item update because the tree can change its top item + // autonomously immediately after the listeners have been notified + getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + tree.setTopItem(treeItem); + }}); + } + }); + + // ensure synchronization of expanded items between tree and time graph + fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() { + @Override + public void treeCollapsed(TimeGraphTreeExpansionEvent event) { + fTreeViewer.setExpandedState(event.getEntry(), false); + } + + @Override + public void treeExpanded(TimeGraphTreeExpansionEvent event) { + fTreeViewer.setExpandedState(event.getEntry(), true); + } + }); + + // 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; + ArrayList treeItems = getVisibleExpandedItems(tree); + 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); + } + } + }); + + // 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); + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + }); + + // prevent key stroke from selecting a filler tree item + tree.addListener(SWT.KeyDown, new Listener() { + @Override + public void handleEvent(Event event) { + ArrayList treeItems = getVisibleExpandedItems(tree); + 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; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + if (fTimeGraphViewer.getSelectionIndex() >= 0) { + fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection())); + } else { + fTreeViewer.setSelection(new StructuredSelection()); + } + } + }); + + // ensure alignment of top item between tree and time graph + fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + }); + + // 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); + } + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + } + }); + + // 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; + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + }); + + // ensure alignment of top item between tree and time graph + fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + }); + + // ensure alignment of top item between tree and time graph + fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() { + @Override + public void mouseScrolled(MouseEvent e) { + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + tree.setTopItem(treeItem); + } + }); + + // ensure the tree has focus control when mouse is over it if the time graph had control + fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() { + @Override + public void mouseEnter(MouseEvent e) { + if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) { + fTreeViewer.getControl().setFocus(); + } + } + }); + + // ensure the time graph has focus control when mouse is over it if the tree had control + fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() { + @Override + public void mouseEnter(MouseEvent e) { + if (fTreeViewer.getControl().isFocusControl()) { + fTimeGraphViewer.getTimeGraphControl().setFocus(); + } + } + }); + fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() { + @Override + public void mouseEnter(MouseEvent e) { + if (fTreeViewer.getControl().isFocusControl()) { + fTimeGraphViewer.getTimeGraphControl().setFocus(); + } + } + }); + + // 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); + + sash.setWeights(new int[] { 1, 1 }); + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns this time graph combo's tree viewer. + * + * @return the tree viewer + */ + public TreeViewer getTreeViewer() { + return fTreeViewer; + } + + /** + * Returns this time graph combo's time graph viewer. + * + * @return the time graph viewer + */ + public TimeGraphViewer getTimeGraphViewer() { + return fTimeGraphViewer; + } + + // ------------------------------------------------------------------------ + // Control + // ------------------------------------------------------------------------ + + /* (non-Javadoc) + * @see org.eclipse.swt.widgets.Control#redraw() + */ + @Override + public void redraw() { + fTimeGraphViewer.getControl().redraw(); + super.redraw(); + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + /** + * Sets the tree content provider used by this time graph combo. + * + * @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 + */ + public void setTreeLabelProvider(ITableLabelProvider labelProvider) { + fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider)); + } + + /** + * Sets the tree columns for this time graph combo. + * + * @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.setText(columnName); + column.pack(); + } + } + + /** + * Sets the time graph provider used by this time graph combo. + * + * @param timeGraphProvider the time graph provider + */ + public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { + fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider); + } + + /** + * Sets or clears the input for this time graph combo. + * The input array should only contain top-level elements. + * + * @param input the input of this time graph combo, or null if none + */ + public void setInput(ITimeGraphEntry[] input) { + fInhibitTreeSelection = true; + fTreeViewer.setInput(input); + for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) { + listenerWrapper.selection = null; + } + fInhibitTreeSelection = false; + fTreeViewer.expandAll(); + fTreeViewer.getTree().getVerticalBar().setEnabled(false); + fTreeViewer.getTree().getVerticalBar().setVisible(false); + fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree())); + fTimeGraphViewer.setInput(input); + } + + /** + * Refreshes this time graph completely with information freshly obtained from its model. + */ + public void refresh() { + fInhibitTreeSelection = true; + fTreeViewer.refresh(); + fTimeGraphViewer.refresh(); + fInhibitTreeSelection = false; + } + + /** + * Adds a listener for selection changes in this time graph combo. + * + * @param listener a selection listener + */ + public void addSelectionListener(ITimeGraphSelectionListener listener) { + SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener); + fTreeViewer.addSelectionChangedListener(listenerWrapper); + fSelectionListenerMap.put(listener, listenerWrapper); + fTimeGraphViewer.addSelectionListener(listenerWrapper); + } + + /** + * Removes the given selection listener from this time graph combo. + * + * @param listener a selection changed listener + */ + public void removeSelectionListener(ITimeGraphSelectionListener listener) { + SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener); + fTreeViewer.removeSelectionChangedListener(listenerWrapper); + fTimeGraphViewer.removeSelectionListener(listenerWrapper); + } + + /** + * Sets the current selection for this time graph combo. + * + * @param selection the new selection + */ + public void setSelection(ITimeGraphEntry selection) { + fTimeGraphViewer.setSelection(selection); + fInhibitTreeSelection = true; // block the tree selection changed listener + if (selection != null) { + StructuredSelection structuredSelection = new StructuredSelection(selection); + fTreeViewer.setSelection(structuredSelection); + } else { + fTreeViewer.setSelection(new StructuredSelection()); + } + fInhibitTreeSelection = false; + ArrayList treeItems = getVisibleExpandedItems(fTreeViewer.getTree()); + if (treeItems.size() == 0) { + return; + } + TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); + fTreeViewer.getTree().setTopItem(treeItem); + } + + /** + * Set the expanded state of an entry + * + * @param entry + * The entry to expand/collapse + * @param expanded + * True for expanded, false for collapsed + * + * @since 2.0 + */ + public void setExpandedState(ITimeGraphEntry entry, boolean expanded) { + fTimeGraphViewer.setExpandedState(entry, expanded); + fTreeViewer.setExpandedState(entry, expanded); + } + + /** + * Collapses all nodes of the viewer's tree, starting with the root. + * + * @since 2.0 + */ + public void collapseAll() { + fTimeGraphViewer.collapseAll(); + fTreeViewer.collapseAll(); + } + + /** + * Expands all nodes of the viewer's tree, starting with the root. + * + * @since 2.0 + */ + public void expandAll() { + fTimeGraphViewer.expandAll(); + fTreeViewer.expandAll(); + } + + // ------------------------------------------------------------------------ + // Internal + // ------------------------------------------------------------------------ + + private ArrayList getVisibleExpandedItems(Tree tree) { + ArrayList items = new ArrayList(); + for (TreeItem item : tree.getItems()) { + if (item.getData() == FILLER) { + break; + } + items.add(item); + if (item.getExpanded()) { + items.addAll(getVisibleExpandedItems(item)); + } + } + return items; + } + + private ArrayList getVisibleExpandedItems(TreeItem treeItem) { + ArrayList items = new ArrayList(); + for (TreeItem item : treeItem.getItems()) { + items.add(item); + if (item.getExpanded()) { + items.addAll(getVisibleExpandedItems(item)); + } + } + return items; + } + + private int getItemHeight(final Tree tree) { + /* + * 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) { + return fLinuxItemHeight; + } + ArrayList treeItems = getVisibleExpandedItems(tree); + if (treeItems.size() > 1) { + final TreeItem treeItem0 = treeItems.get(0); + final TreeItem treeItem1 = treeItems.get(1); + PaintListener paintListener = new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + tree.removePaintListener(this); + int y0 = treeItem0.getBounds().y; + int y1 = treeItem1.getBounds().y; + int itemHeight = y1 - y0; + if (itemHeight > 0) { + fLinuxItemHeight = itemHeight; + fTimeGraphViewer.setItemHeight(itemHeight); + } + } + }; + tree.addPaintListener(paintListener); + } + } else { + fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore + } + return tree.getItemHeight(); + } + +} diff --git a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphViewer.java b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphViewer.java index ca3c629a53..d3e9151ce1 100644 --- a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphViewer.java +++ b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphViewer.java @@ -1,1340 +1,1360 @@ -/***************************************************************************** - * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 Ericsson. - * 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 - * - * Contributors: - * Intel Corporation - Initial API and implementation - * Ruslan A. Scherbakov, Intel - Initial API and implementation - * Alexander N. Alexeev, Intel - Add monitors statistics support - * Alvaro Sanchez-Leon - Adapted for TMF - * Patrick Tasse - Refactoring - * - *****************************************************************************/ - -package org.eclipse.linuxtools.tmf.ui.widgets.timegraph; - -import java.util.ArrayList; - -import org.eclipse.jface.action.Action; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.linuxtools.internal.tmf.ui.Activator; -import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants; -import org.eclipse.linuxtools.internal.tmf.ui.Messages; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphLegend; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphControl; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphScale; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphTooltipHandler; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.KeyAdapter; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseWheelListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.ScrollBar; -import org.eclipse.swt.widgets.Slider; - -/** - * Generic time graph viewer implementation - * - * @version 1.0 - * @author Patrick Tasse, and others - */ -public class TimeGraphViewer implements ITimeDataProvider, SelectionListener { - - /** vars */ - private long _minTimeInterval; - private long _selectedTime; - private ITimeGraphEntry _selectedEntry; - private long _beginTime; - private long _endTime; - private long _time0; - private long _time1; - private long _time0_; - private long _time1_; - private long _time0_extSynch = 0; - private long _time1_extSynch = 0; - private boolean _timeRangeFixed; - private int _nameWidthPref = 200; - private int _minNameWidth = 6; - private int _nameWidth; - private Composite _dataViewer; - - private TimeGraphControl _stateCtrl; - private TimeGraphScale _timeScaleCtrl; - private Slider _verticalScrollBar; - private TimeGraphTooltipHandler _threadTip; - private TimeGraphColorScheme _colors; - private ITimeGraphPresentationProvider fTimeGraphProvider; - - ArrayList fSelectionListeners = new ArrayList(); - ArrayList fTimeListeners = new ArrayList(); - ArrayList fRangeListeners = new ArrayList(); - - // Calender Time format, using Epoch reference or Relative time - // format(default - private boolean calendarTimeFormat = false; - private int borderWidth = 0; - private int timeScaleHeight = 22; - - private Action resetScale; - private Action showLegendAction; - private Action nextEventAction; - private Action prevEventAction; - private Action nextItemAction; - private Action previousItemAction; - private Action zoomInAction; - private Action zoomOutAction; - - /** - * Standard constructor - * - * @param parent - * The parent UI composite object - * @param style - * The style to use - */ - public TimeGraphViewer(Composite parent, int style) { - createDataViewer(parent, style); - } - - /** - * Sets the timegraph provider used by this timegraph viewer. - * - * @param timeGraphProvider the timegraph provider - */ - public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { - fTimeGraphProvider = timeGraphProvider; - _stateCtrl.setTimeGraphProvider(timeGraphProvider); - _threadTip = new TimeGraphTooltipHandler(_dataViewer.getShell(), fTimeGraphProvider, this); - _threadTip.activateHoverHelp(_stateCtrl); - } - - /** - * Sets or clears the input for this time graph viewer. - * The input array should only contain top-level elements. - * - * @param input the input of this time graph viewer, or null if none - */ - public void setInput(ITimeGraphEntry[] input) { - if (null != _stateCtrl) { - if (null == input) { - input = new ITimeGraphEntry[0]; - } - setTimeRange(input); - _verticalScrollBar.setEnabled(true); - setTopIndex(0); - _selectedTime = 0; - _selectedEntry = null; - refreshAllData(input); - } - } - - /** - * Refresh the view - */ - public void refresh() { - setInput(_stateCtrl.getTraces()); - } - - /** - * Callback for when the control is moved - * - * @param e - * The caller event - */ - public void controlMoved(ControlEvent e) { - } - - /** - * Callback for when the control is resized - * - * @param e - * The caller event - */ - public void controlResized(ControlEvent e) { - resizeControls(); - } - - /** - * Handler for when the model is updated. Called from the display order in - * the API - * - * @param traces - * The traces in the model - * @param start - * The start time - * @param end - * The end time - * @param updateTimeBounds - * Should we updated the time bounds too - */ - public void modelUpdate(ITimeGraphEntry[] traces, long start, - long end, boolean updateTimeBounds) { - if (null != _stateCtrl) { - //loadOptions(); - updateInternalData(traces, start, end); - if (updateTimeBounds) { - _timeRangeFixed = true; - // set window to match limits - setStartFinishTime(_time0_, _time1_); - } else { - _stateCtrl.redraw(); - _timeScaleCtrl.redraw(); - } - } - } - - protected String getViewTypeStr() { - return "viewoption.threads"; //$NON-NLS-1$ - } - - int getMarginWidth(int idx) { - return 0; - } - - int getMarginHeight(int idx) { - return 0; - } - - void loadOptions() { - _minTimeInterval = 1; - _selectedTime = -1; - _nameWidth = Utils.loadIntOption(getPreferenceString("namewidth"), //$NON-NLS-1$ - _nameWidthPref, _minNameWidth, 1000); - } - - void saveOptions() { - Utils.saveIntOption(getPreferenceString("namewidth"), _nameWidth); //$NON-NLS-1$ - } - - protected Control createDataViewer(Composite parent, int style) { - loadOptions(); - _colors = new TimeGraphColorScheme(); - _dataViewer = new Composite(parent, style) { - @Override - public void redraw() { - _timeScaleCtrl.redraw(); - _stateCtrl.redraw(); - super.redraw(); - } - }; - GridLayout gl = new GridLayout(2, false); - gl.marginHeight = borderWidth; - gl.marginWidth = 0; - gl.verticalSpacing = 0; - gl.horizontalSpacing = 0; - _dataViewer.setLayout(gl); - - _timeScaleCtrl = new TimeGraphScale(_dataViewer, _colors); - _timeScaleCtrl.setTimeProvider(this); - _timeScaleCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false)); - _timeScaleCtrl.setHeight(timeScaleHeight); - - _verticalScrollBar = new Slider(_dataViewer, SWT.VERTICAL | SWT.NO_FOCUS); - _verticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 2)); - _verticalScrollBar.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - setTopIndex(_verticalScrollBar.getSelection()); - } - }); - _verticalScrollBar.setEnabled(false); - - _stateCtrl = createTimeGraphControl(); - - _stateCtrl.setTimeProvider(this); - _stateCtrl.addSelectionListener(this); - _stateCtrl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2)); - _stateCtrl.addMouseWheelListener(new MouseWheelListener() { - @Override - public void mouseScrolled(MouseEvent e) { - adjustVerticalScrollBar(); - } - }); - _stateCtrl.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - adjustVerticalScrollBar(); - } - }); - - Composite filler = new Composite(_dataViewer, SWT.NONE); - GridData gd = new GridData(SWT.DEFAULT, SWT.DEFAULT, false, false); - gd.heightHint = _stateCtrl.getHorizontalBar().getSize().y; - filler.setLayoutData(gd); - filler.setLayout(new FillLayout()); - - _stateCtrl.addControlListener(new ControlAdapter() { - @Override - public void controlResized(ControlEvent event) { - resizeControls(); - } - }); - resizeControls(); - _dataViewer.update(); - adjustVerticalScrollBar(); - return _dataViewer; - } - - /** - * Dispose the view. - */ - public void dispose() { - saveOptions(); - _stateCtrl.dispose(); - _dataViewer.dispose(); - _colors.dispose(); - } - - protected TimeGraphControl createTimeGraphControl() { - return new TimeGraphControl(_dataViewer, _colors); - } - - /** - * Resize the controls - */ - public void resizeControls() { - Rectangle r = _dataViewer.getClientArea(); - if (r.isEmpty()) { - return; - } - - int width = r.width; - if (_nameWidth > width - _minNameWidth) { - _nameWidth = width - _minNameWidth; - } - if (_nameWidth < _minNameWidth) { - _nameWidth = _minNameWidth; - } - adjustVerticalScrollBar(); - } - - /** - * Try to set most convenient time range for display. - * - * @param traces - * The traces in the model - */ - public void setTimeRange(ITimeGraphEntry traces[]) { - _endTime = 0; - _beginTime = -1; - for (int i = 0; i < traces.length; i++) { - ITimeGraphEntry entry = traces[i]; - if (entry.getEndTime() >= entry.getStartTime() && entry.getEndTime() > 0) { - if (_beginTime < 0 || entry.getStartTime() < _beginTime) { - _beginTime = entry.getStartTime(); - } - if (entry.getEndTime() > _endTime) { - _endTime = entry.getEndTime(); - } - } - } - - if (_beginTime < 0) { - _beginTime = 0; - } - } - - /** - * Recalculate the time bounds - */ - public void setTimeBounds() { - //_time0_ = _beginTime - (long) ((_endTime - _beginTime) * 0.02); - _time0_ = _beginTime; - if (_time0_ < 0) { - _time0_ = 0; - } - // _time1_ = _time0_ + (_endTime - _time0_) * 1.05; - _time1_ = _endTime; - // _time0_ = Math.floor(_time0_); - // _time1_ = Math.ceil(_time1_); - if (!_timeRangeFixed) { - _time0 = _time0_; - _time1 = _time1_; - } - if (_time1 - _time0 < _minTimeInterval) { - _time1 = Math.min(_time1_, _time0 + _minTimeInterval); - } - } - - /** - * @param traces - * @param start - * @param end - */ - void updateInternalData(ITimeGraphEntry[] traces, long start, long end) { - if (null == traces) { - traces = new ITimeGraphEntry[0]; - } - if ((start == 0 && end == 0) || start < 0 || end < 0) { - // Start and end time are unspecified and need to be determined from - // individual processes - setTimeRange(traces); - } else { - _beginTime = start; - _endTime = end; - } - - refreshAllData(traces); - } - - /** - * @param traces - */ - private void refreshAllData(ITimeGraphEntry[] traces) { - setTimeBounds(); - if (_selectedTime < _beginTime) { - _selectedTime = _beginTime; - } else if (_selectedTime > _endTime) { - _selectedTime = _endTime; - } - _stateCtrl.refreshData(traces); - _timeScaleCtrl.redraw(); - adjustVerticalScrollBar(); - } - - /** - * Callback for when this view is focused - */ - public void setFocus() { - if (null != _stateCtrl) { - _stateCtrl.setFocus(); - } - } - - /** - * Get the current focus status of this view. - * - * @return If the view is currently focused, or not - */ - public boolean isInFocus() { - return _stateCtrl.isInFocus(); - } - - /** - * Get the view's current selection - * - * @return The entry that is selected - */ - public ITimeGraphEntry getSelection() { - return _stateCtrl.getSelectedTrace(); - } - - /** - * Get the index of the current selection - * - * @return The index - */ - public int getSelectionIndex() { - return _stateCtrl.getSelectedIndex(); - } - - @Override - public long getTime0() { - return _time0; - } - - @Override - public long getTime1() { - return _time1; - } - - @Override - public long getMinTimeInterval() { - return _minTimeInterval; - } - - @Override - public int getNameSpace() { - return _nameWidth; - } - - @Override - public void setNameSpace(int width) { - _nameWidth = width; - width = _stateCtrl.getClientArea().width; - if (_nameWidth > width - 6) { - _nameWidth = width - 6; - } - if (_nameWidth < 6) { - _nameWidth = 6; - } - _stateCtrl.adjustScrolls(); - _stateCtrl.redraw(); - _timeScaleCtrl.redraw(); - } - - @Override - public int getTimeSpace() { - int w = _stateCtrl.getClientArea().width; - return w - _nameWidth; - } - - @Override - public long getSelectedTime() { - return _selectedTime; - } - - @Override - public long getBeginTime() { - return _beginTime; - } - - @Override - public long getEndTime() { - return _endTime; - } - - @Override - public long getMaxTime() { - return _time1_; - } - - @Override - public long getMinTime() { - return _time0_; - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider - * #setStartFinishTimeNotify(long, long) - */ - @Override - public void setStartFinishTimeNotify(long time0, long time1) { - setStartFinishTime(time0, time1); - notifyRangeListeners(time0, time1); - } - - - /* (non-Javadoc) - * @see org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider#notifyStartFinishTime() - */ - @Override - public void notifyStartFinishTime() { - notifyRangeListeners(_time0, _time1); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider - * #setStartFinishTime(long, long) - */ - @Override - public void setStartFinishTime(long time0, long time1) { - _time0 = time0; - if (_time0 < _time0_) { - _time0 = _time0_; - } - if (_time0 > _time1_) { - _time0 = _time1_; - } - _time1 = time1; - if (_time1 < _time0_) { - _time1 = _time0_; - } - if (_time1 > _time1_) { - _time1 = _time1_; - } - if (_time1 - _time0 < _minTimeInterval) { - _time1 = Math.min(_time1_, _time0 + _minTimeInterval); - } - _timeRangeFixed = true; - _stateCtrl.adjustScrolls(); - _stateCtrl.redraw(); - _timeScaleCtrl.redraw(); - } - - /** - * Set the time bounds to the provided values - * - * @param beginTime - * The start time of the window - * @param endTime - * The end time - */ - public void setTimeBounds(long beginTime, long endTime) { - _beginTime = beginTime; - _endTime = endTime; - _time0_ = beginTime; - _time1_ = endTime; - _stateCtrl.adjustScrolls(); - } - - @Override - public void resetStartFinishTime() { - setStartFinishTimeNotify(_time0_, _time1_); - _timeRangeFixed = false; - } - - @Override - public void setSelectedTimeNotify(long time, boolean ensureVisible) { - setSelectedTimeInt(time, ensureVisible, true); - } - - @Override - public void setSelectedTime(long time, boolean ensureVisible) { - setSelectedTimeInt(time, ensureVisible, false); - } - - private void setSelectedTimeInt(long time, boolean ensureVisible, boolean doNotify) { - long time0 = _time0; - long time1 = _time1; - if (ensureVisible) { - long timeSpace = (long) ((_time1 - _time0) * .02); - long timeMid = (long) ((_time1 - _time0) * .5); - if (time < _time0 + timeSpace) { - long dt = _time0 - time + timeMid; - _time0 -= dt; - _time1 -= dt; - } else if (time > _time1 - timeSpace) { - long dt = time - _time1 + timeMid; - _time0 += dt; - _time1 += dt; - } - if (_time0 < _time0_) { - _time1 = Math.min(_time1_, _time1 + (_time0_ - _time0)); - _time0 = _time0_; - } else if (_time1 > _time1_) { - _time0 = Math.max(_time0_, _time0 - (_time1 - _time1_)); - _time1 = _time1_; - } - } - if (_time1 - _time0 < _minTimeInterval) { - _time1 = Math.min(_time1_, _time0 + _minTimeInterval); - } - _stateCtrl.adjustScrolls(); - _stateCtrl.redraw(); - _timeScaleCtrl.redraw(); - - - boolean notifySelectedTime = (time != _selectedTime); - _selectedTime = time; - - if (doNotify && ((time0 != _time0) || (time1 != _time1))) { - notifyRangeListeners(_time0, _time1); - } - - if (doNotify && notifySelectedTime) { - notifyTimeListeners(_selectedTime); - } - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - if (_selectedEntry != getSelection()) { - _selectedEntry = getSelection(); - notifySelectionListeners(_selectedEntry); - } - } - - @Override - public void widgetSelected(SelectionEvent e) { - if (_selectedEntry != getSelection()) { - _selectedEntry = getSelection(); - notifySelectionListeners(_selectedEntry); - } - } - - /** - * Callback for when the next event is selected - */ - public void selectNextEvent() { - _stateCtrl.selectNextEvent(); - adjustVerticalScrollBar(); - } - - /** - * Callback for when the previous event is selected - */ - public void selectPrevEvent() { - _stateCtrl.selectPrevEvent(); - adjustVerticalScrollBar(); - } - - /** - * Callback for when the next item is selected - */ - public void selectNextItem() { - _stateCtrl.selectNextTrace(); - adjustVerticalScrollBar(); - } - - /** - * Callback for when the previous item is selected - */ - public void selectPrevItem() { - _stateCtrl.selectPrevTrace(); - adjustVerticalScrollBar(); - } - - /** - * Callback for the show legend action - */ - public void showLegend() { - if (_dataViewer == null || _dataViewer.isDisposed()) { - return; - } - - TimeGraphLegend.open(_dataViewer.getShell(), fTimeGraphProvider); - } - - /** - * Callback for the Zoom In action - */ - public void zoomIn() { - _stateCtrl.zoomIn(); - } - - /** - * Callback for the Zoom Out action - */ - public void zoomOut() { - _stateCtrl.zoomOut(); - } - - private String getPreferenceString(String string) { - return getViewTypeStr() + "." + string; //$NON-NLS-1$ - } - - /** - * Add a selection listener - * - * @param listener - * The listener to add - */ - public void addSelectionListener(ITimeGraphSelectionListener listener) { - fSelectionListeners.add(listener); - } - - /** - * Remove a selection listener - * - * @param listener - * The listener to remove - */ - public void removeSelectionListener(ITimeGraphSelectionListener listener) { - fSelectionListeners.remove(listener); - } - - private void notifySelectionListeners(ITimeGraphEntry selection) { - TimeGraphSelectionEvent event = new TimeGraphSelectionEvent(this, selection); - - for (ITimeGraphSelectionListener listener : fSelectionListeners) { - listener.selectionChanged(event); - } - } - - /** - * Add a time listener - * - * @param listener - * The listener to add - */ - public void addTimeListener(ITimeGraphTimeListener listener) { - fTimeListeners.add(listener); - } - - /** - * Remove a time listener - * - * @param listener - * The listener to remove - */ - public void removeTimeListener(ITimeGraphTimeListener listener) { - fTimeListeners.remove(listener); - } - - private void notifyTimeListeners(long time) { - TimeGraphTimeEvent event = new TimeGraphTimeEvent(this, time); - - for (ITimeGraphTimeListener listener : fTimeListeners) { - listener.timeSelected(event); - } - } - - /** - * Add a range listener - * - * @param listener - * The listener to add - */ - public void addRangeListener(ITimeGraphRangeListener listener) { - fRangeListeners.add(listener); - } - - /** - * Remove a range listener - * - * @param listener - * The listener to remove - */ - public void removeRangeListener(ITimeGraphRangeListener listener) { - fRangeListeners.remove(listener); - } - - private void notifyRangeListeners(long startTime, long endTime) { - // Check if the time has actually changed from last notification - if (startTime != _time0_extSynch || endTime != _time1_extSynch) { - // Notify Time Scale Selection Listeners - TimeGraphRangeUpdateEvent event = new TimeGraphRangeUpdateEvent(this, startTime, endTime); - - for (ITimeGraphRangeListener listener : fRangeListeners) { - listener.timeRangeUpdated(event); - } - - // update external synch timers - updateExtSynchTimers(); - } - } - - /** - * Callback to set a selected event in the view - * - * @param event - * The event that was selected - * @param source - * The source of this selection event - */ - public void setSelectedEvent(ITimeEvent event, Object source) { - if (event == null || source == this) { - return; - } - _selectedEntry = event.getEntry(); - _stateCtrl.selectItem(_selectedEntry, false); - - setSelectedTimeInt(event.getTime(), true, true); - adjustVerticalScrollBar(); - } - - /** - * Set the seeked time of a trace - * - * @param trace - * The trace that was seeked - * @param time - * The target time - * @param source - * The source of this seek event - */ - public void setSelectedTraceTime(ITimeGraphEntry trace, long time, Object source) { - if (trace == null || source == this) { - return; - } - _selectedEntry = trace; - _stateCtrl.selectItem(trace, false); - - setSelectedTimeInt(time, true, true); - } - - /** - * Callback for a trace selection - * - * @param trace - * The trace that was selected - */ - public void setSelection(ITimeGraphEntry trace) { - _selectedEntry = trace; - _stateCtrl.selectItem(trace, false); - adjustVerticalScrollBar(); - } - - /** - * Callback for a time window selection - * - * @param time0 - * Start time of the range - * @param time1 - * End time of the range - * @param source - * Source of the event - */ - public void setSelectVisTimeWindow(long time0, long time1, Object source) { - if (source == this) { - return; - } - - setStartFinishTime(time0, time1); - - // update notification time values since we are now in synch with the - // external application - updateExtSynchTimers(); - } - - /** - * update the cache timers used to identify the need to send a time window - * update to external registered listeners - */ - private void updateExtSynchTimers() { - // last time notification cache - _time0_extSynch = _time0; - _time1_extSynch = _time1; - } - - /** - * Set the calendar format - * - * @param toAbsoluteCaltime - * True for absolute time, false for relative - */ - public void setTimeCalendarFormat(boolean toAbsoluteCaltime) { - calendarTimeFormat = toAbsoluteCaltime; - } - - @Override - public boolean isCalendarFormat() { - return calendarTimeFormat; - } - - /** - * Retrieve the border width - * - * @return The width - */ - public int getBorderWidth() { - return borderWidth; - } - - /** - * Set the border width - * - * @param borderWidth - * The width - */ - public void setBorderWidth(int borderWidth) { - if (borderWidth > -1) { - this.borderWidth = borderWidth; - GridLayout gl = (GridLayout)_dataViewer.getLayout(); - gl.marginHeight = borderWidth; - } - } - - /** - * Retrieve the height of the header - * - * @return The height - */ - public int getHeaderHeight() { - return timeScaleHeight; - } - - /** - * Set the height of the header - * - * @param headerHeight - * The height to set - */ - public void setHeaderHeight(int headerHeight) { - if (headerHeight > -1) { - this.timeScaleHeight = headerHeight; - _timeScaleCtrl.setHeight(headerHeight); - } - } - - /** - * Retrieve the height of an item row - * - * @return The height - */ - public int getItemHeight() { - if (_stateCtrl != null) { - return _stateCtrl.getItemHeight(); - } - return 0; - } - - /** - * Set the height of an item row - * - * @param rowHeight - * The height to set - */ - public void setItemHeight(int rowHeight) { - if (_stateCtrl != null) { - _stateCtrl.setItemHeight(rowHeight); - } - } - - /** - * Set the minimum item width - * - * @param width - * The min width - */ - public void setMinimumItemWidth(int width) { - if (_stateCtrl != null) { - _stateCtrl.setMinimumItemWidth(width); - } - } - - /** - * Set the width for the name column - * - * @param width The width - */ - public void setNameWidthPref(int width) { - _nameWidthPref = width; - if (width == 0) { - _minNameWidth = 0; - _nameWidth = 0; - } - } - - /** - * Retrieve the configure width for the name column - * - * @param width - * Unused? - * @return The width - */ - public int getNameWidthPref(int width) { - return _nameWidthPref; - } - - /** - * Returns the primary control associated with this viewer. - * - * @return the SWT control which displays this viewer's content - */ - public Control getControl() { - return _dataViewer; - } - - /** - * Returns the time graph control associated with this viewer. - * - * @return the time graph control - */ - TimeGraphControl getTimeGraphControl() { - return _stateCtrl; - } - - /** - * Returns the time graph scale associated with this viewer. - * - * @return the time graph scale - */ - TimeGraphScale getTimeGraphScale() { - return _timeScaleCtrl; - } - - /** - * Get the selection provider - * - * @return the selection provider - */ - public ISelectionProvider getSelectionProvider() { - return _stateCtrl; - } - - /** - * Wait for the cursor - * - * @param waitInd - * Wait indefinitely? - */ - public void waitCursor(boolean waitInd) { - _stateCtrl.waitCursor(waitInd); - } - - /** - * Get the horizontal scroll bar object - * - * @return The scroll bar - */ - public ScrollBar getHorizontalBar() { - return _stateCtrl.getHorizontalBar(); - } - - /** - * Get the vertical scroll bar object - * - * @return The scroll bar - */ - public Slider getVerticalBar() { - return _verticalScrollBar; - } - - /** - * Set the given index as the top one - * - * @param index - * The index that will go to the top - */ - public void setTopIndex(int index) { - _stateCtrl.setTopIndex(index); - adjustVerticalScrollBar(); - } - - /** - * Retrieve the current top index - * - * @return The top index - */ - public int getTopIndex() { - return _stateCtrl.getTopIndex(); - } - - /** - * Set the expanded state of an entry - * - * @param entry - * The entry to expand/collapse - * @param expanded - * True for expanded, false for collapsed - */ - public void setExpandedState(ITimeGraphEntry entry, boolean expanded) { - _stateCtrl.setExpandedState(entry, expanded); - adjustVerticalScrollBar(); - } - - /** - * Get the number of sub-elements when expanded - * - * @return The element count - */ - public int getExpandedElementCount() { - return _stateCtrl.getExpandedElementCount(); - } - - /** - * Get the sub-elements - * - * @return The array of entries that are below this one - */ - public ITimeGraphEntry[] getExpandedElements() { - return _stateCtrl.getExpandedElements(); - } - - /** - * Add a tree listener - * - * @param listener - * The listener to add - */ - public void addTreeListener(ITimeGraphTreeListener listener) { - _stateCtrl.addTreeListener(listener); - } - - /** - * Remove a tree listener - * - * @param listener - * The listener to remove - */ - public void removeTreeListener(ITimeGraphTreeListener listener) { - _stateCtrl.removeTreeListener(listener); - } - - /** - * Get the reset scale action. - * - * @return The Action object - */ - public Action getResetScaleAction() { - if (resetScale == null) { - // resetScale - resetScale = new Action() { - @Override - public void run() { - resetStartFinishTime(); - } - }; - resetScale.setText(Messages.TmfTimeGraphViewer_ResetScaleActionNameText); - resetScale.setToolTipText(Messages.TmfTimeGraphViewer_ResetScaleActionToolTipText); - resetScale.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_HOME_MENU)); - } - return resetScale; - } - - /** - * Get the show legend action. - * - * @return The Action object - */ - public Action getShowLegendAction() { - if (showLegendAction == null) { - // showLegend - showLegendAction = new Action() { - @Override - public void run() { - showLegend(); - } - }; - showLegendAction.setText(Messages.TmfTimeGraphViewer_LegendActionNameText); - showLegendAction.setToolTipText(Messages.TmfTimeGraphViewer_LegendActionToolTipText); - showLegendAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LEGEND)); - } - - return showLegendAction; - } - - /** - * Get the the next event action. - * - * @return The action object - */ - public Action getNextEventAction() { - if (nextEventAction == null) { - nextEventAction = new Action() { - @Override - public void run() { - selectNextEvent(); - } - }; - - nextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText); - nextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText); - nextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT)); - } - - return nextEventAction; - } - - /** - * Get the previous event action. - * - * @return The Action object - */ - public Action getPreviousEventAction() { - if (prevEventAction == null) { - prevEventAction = new Action() { - @Override - public void run() { - selectPrevEvent(); - } - }; - - prevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText); - prevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText); - prevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT)); - } - - return prevEventAction; - } - - /** - * Get the next item action. - * - * @return The Action object - */ - public Action getNextItemAction() { - if (nextItemAction == null) { - - nextItemAction = new Action() { - @Override - public void run() { - selectNextItem(); - } - }; - nextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText); - nextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText); - nextItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_ITEM)); - } - return nextItemAction; - } - - /** - * Get the previous item action. - * - * @return The Action object - */ - public Action getPreviousItemAction() { - if (previousItemAction == null) { - - previousItemAction = new Action() { - @Override - public void run() { - selectPrevItem(); - } - }; - previousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText); - previousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText); - previousItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_ITEM)); - } - return previousItemAction; - } - - /** - * Get the zoom in action - * - * @return The Action object - */ - public Action getZoomInAction() { - if (zoomInAction == null) { - zoomInAction = new Action() { - @Override - public void run() { - zoomIn(); - } - }; - zoomInAction.setText(Messages.TmfTimeGraphViewer_ZoomInActionNameText); - zoomInAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomInActionToolTipText); - zoomInAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_IN_MENU)); - } - return zoomInAction; - } - - /** - * Get the zoom out action - * - * @return The Action object - */ - public Action getZoomOutAction() { - if (zoomOutAction == null) { - zoomOutAction = new Action() { - @Override - public void run() { - zoomOut(); - } - }; - zoomOutAction.setText(Messages.TmfTimeGraphViewer_ZoomOutActionNameText); - zoomOutAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomOutActionToolTipText); - zoomOutAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_OUT_MENU)); - } - return zoomOutAction; - } - - - private void adjustVerticalScrollBar() { - int topIndex = _stateCtrl.getTopIndex(); - int countPerPage = _stateCtrl.countPerPage(); - int expandedElementCount = _stateCtrl.getExpandedElementCount(); - if (topIndex + countPerPage > expandedElementCount) { - _stateCtrl.setTopIndex(Math.max(0, expandedElementCount - countPerPage)); - } - - int selection = _stateCtrl.getTopIndex(); - int min = 0; - int max = Math.max(1, expandedElementCount - 1); - int thumb = Math.min(max, Math.max(1, countPerPage - 1)); - int increment = 1; - int pageIncrement = Math.max(1, countPerPage); - _verticalScrollBar.setValues(selection, min, max, thumb, increment, pageIncrement); - } - - - -} +/***************************************************************************** + * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 Ericsson. + * 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 + * + * Contributors: + * Intel Corporation - Initial API and implementation + * Ruslan A. Scherbakov, Intel - Initial API and implementation + * Alexander N. Alexeev, Intel - Add monitors statistics support + * Alvaro Sanchez-Leon - Adapted for TMF + * Patrick Tasse - Refactoring + * + *****************************************************************************/ + +package org.eclipse.linuxtools.tmf.ui.widgets.timegraph; + +import java.util.ArrayList; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.linuxtools.internal.tmf.ui.Activator; +import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants; +import org.eclipse.linuxtools.internal.tmf.ui.Messages; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphLegend; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphControl; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphScale; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphTooltipHandler; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseWheelListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Slider; + +/** + * Generic time graph viewer implementation + * + * @version 1.0 + * @author Patrick Tasse, and others + */ +public class TimeGraphViewer implements ITimeDataProvider, SelectionListener { + + /** vars */ + private long _minTimeInterval; + private long _selectedTime; + private ITimeGraphEntry _selectedEntry; + private long _beginTime; + private long _endTime; + private long _time0; + private long _time1; + private long _time0_; + private long _time1_; + private long _time0_extSynch = 0; + private long _time1_extSynch = 0; + private boolean _timeRangeFixed; + private int _nameWidthPref = 200; + private int _minNameWidth = 6; + private int _nameWidth; + private Composite _dataViewer; + + private TimeGraphControl _stateCtrl; + private TimeGraphScale _timeScaleCtrl; + private Slider _verticalScrollBar; + private TimeGraphTooltipHandler _threadTip; + private TimeGraphColorScheme _colors; + private ITimeGraphPresentationProvider fTimeGraphProvider; + + ArrayList fSelectionListeners = new ArrayList(); + ArrayList fTimeListeners = new ArrayList(); + ArrayList fRangeListeners = new ArrayList(); + + // Calender Time format, using Epoch reference or Relative time + // format(default + private boolean calendarTimeFormat = false; + private int borderWidth = 0; + private int timeScaleHeight = 22; + + private Action resetScale; + private Action showLegendAction; + private Action nextEventAction; + private Action prevEventAction; + private Action nextItemAction; + private Action previousItemAction; + private Action zoomInAction; + private Action zoomOutAction; + + /** + * Standard constructor + * + * @param parent + * The parent UI composite object + * @param style + * The style to use + */ + public TimeGraphViewer(Composite parent, int style) { + createDataViewer(parent, style); + } + + /** + * Sets the timegraph provider used by this timegraph viewer. + * + * @param timeGraphProvider the timegraph provider + */ + public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { + fTimeGraphProvider = timeGraphProvider; + _stateCtrl.setTimeGraphProvider(timeGraphProvider); + _threadTip = new TimeGraphTooltipHandler(_dataViewer.getShell(), fTimeGraphProvider, this); + _threadTip.activateHoverHelp(_stateCtrl); + } + + /** + * Sets or clears the input for this time graph viewer. + * The input array should only contain top-level elements. + * + * @param input the input of this time graph viewer, or null if none + */ + public void setInput(ITimeGraphEntry[] input) { + if (null != _stateCtrl) { + if (null == input) { + input = new ITimeGraphEntry[0]; + } + setTimeRange(input); + _verticalScrollBar.setEnabled(true); + setTopIndex(0); + _selectedTime = 0; + _selectedEntry = null; + refreshAllData(input); + } + } + + /** + * Refresh the view + */ + public void refresh() { + setInput(_stateCtrl.getTraces()); + } + + /** + * Callback for when the control is moved + * + * @param e + * The caller event + */ + public void controlMoved(ControlEvent e) { + } + + /** + * Callback for when the control is resized + * + * @param e + * The caller event + */ + public void controlResized(ControlEvent e) { + resizeControls(); + } + + /** + * Handler for when the model is updated. Called from the display order in + * the API + * + * @param traces + * The traces in the model + * @param start + * The start time + * @param end + * The end time + * @param updateTimeBounds + * Should we updated the time bounds too + */ + public void modelUpdate(ITimeGraphEntry[] traces, long start, + long end, boolean updateTimeBounds) { + if (null != _stateCtrl) { + //loadOptions(); + updateInternalData(traces, start, end); + if (updateTimeBounds) { + _timeRangeFixed = true; + // set window to match limits + setStartFinishTime(_time0_, _time1_); + } else { + _stateCtrl.redraw(); + _timeScaleCtrl.redraw(); + } + } + } + + protected String getViewTypeStr() { + return "viewoption.threads"; //$NON-NLS-1$ + } + + int getMarginWidth(int idx) { + return 0; + } + + int getMarginHeight(int idx) { + return 0; + } + + void loadOptions() { + _minTimeInterval = 1; + _selectedTime = -1; + _nameWidth = Utils.loadIntOption(getPreferenceString("namewidth"), //$NON-NLS-1$ + _nameWidthPref, _minNameWidth, 1000); + } + + void saveOptions() { + Utils.saveIntOption(getPreferenceString("namewidth"), _nameWidth); //$NON-NLS-1$ + } + + protected Control createDataViewer(Composite parent, int style) { + loadOptions(); + _colors = new TimeGraphColorScheme(); + _dataViewer = new Composite(parent, style) { + @Override + public void redraw() { + _timeScaleCtrl.redraw(); + _stateCtrl.redraw(); + super.redraw(); + } + }; + GridLayout gl = new GridLayout(2, false); + gl.marginHeight = borderWidth; + gl.marginWidth = 0; + gl.verticalSpacing = 0; + gl.horizontalSpacing = 0; + _dataViewer.setLayout(gl); + + _timeScaleCtrl = new TimeGraphScale(_dataViewer, _colors); + _timeScaleCtrl.setTimeProvider(this); + _timeScaleCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false)); + _timeScaleCtrl.setHeight(timeScaleHeight); + + _verticalScrollBar = new Slider(_dataViewer, SWT.VERTICAL | SWT.NO_FOCUS); + _verticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 2)); + _verticalScrollBar.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + setTopIndex(_verticalScrollBar.getSelection()); + } + }); + _verticalScrollBar.setEnabled(false); + + _stateCtrl = createTimeGraphControl(); + + _stateCtrl.setTimeProvider(this); + _stateCtrl.addSelectionListener(this); + _stateCtrl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2)); + _stateCtrl.addMouseWheelListener(new MouseWheelListener() { + @Override + public void mouseScrolled(MouseEvent e) { + adjustVerticalScrollBar(); + } + }); + _stateCtrl.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + adjustVerticalScrollBar(); + } + }); + + Composite filler = new Composite(_dataViewer, SWT.NONE); + GridData gd = new GridData(SWT.DEFAULT, SWT.DEFAULT, false, false); + gd.heightHint = _stateCtrl.getHorizontalBar().getSize().y; + filler.setLayoutData(gd); + filler.setLayout(new FillLayout()); + + _stateCtrl.addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent event) { + resizeControls(); + } + }); + resizeControls(); + _dataViewer.update(); + adjustVerticalScrollBar(); + return _dataViewer; + } + + /** + * Dispose the view. + */ + public void dispose() { + saveOptions(); + _stateCtrl.dispose(); + _dataViewer.dispose(); + _colors.dispose(); + } + + protected TimeGraphControl createTimeGraphControl() { + return new TimeGraphControl(_dataViewer, _colors); + } + + /** + * Resize the controls + */ + public void resizeControls() { + Rectangle r = _dataViewer.getClientArea(); + if (r.isEmpty()) { + return; + } + + int width = r.width; + if (_nameWidth > width - _minNameWidth) { + _nameWidth = width - _minNameWidth; + } + if (_nameWidth < _minNameWidth) { + _nameWidth = _minNameWidth; + } + adjustVerticalScrollBar(); + } + + /** + * Try to set most convenient time range for display. + * + * @param traces + * The traces in the model + */ + public void setTimeRange(ITimeGraphEntry traces[]) { + _endTime = 0; + _beginTime = -1; + for (int i = 0; i < traces.length; i++) { + ITimeGraphEntry entry = traces[i]; + if (entry.getEndTime() >= entry.getStartTime() && entry.getEndTime() > 0) { + if (_beginTime < 0 || entry.getStartTime() < _beginTime) { + _beginTime = entry.getStartTime(); + } + if (entry.getEndTime() > _endTime) { + _endTime = entry.getEndTime(); + } + } + } + + if (_beginTime < 0) { + _beginTime = 0; + } + } + + /** + * Recalculate the time bounds + */ + public void setTimeBounds() { + //_time0_ = _beginTime - (long) ((_endTime - _beginTime) * 0.02); + _time0_ = _beginTime; + if (_time0_ < 0) { + _time0_ = 0; + } + // _time1_ = _time0_ + (_endTime - _time0_) * 1.05; + _time1_ = _endTime; + // _time0_ = Math.floor(_time0_); + // _time1_ = Math.ceil(_time1_); + if (!_timeRangeFixed) { + _time0 = _time0_; + _time1 = _time1_; + } + if (_time1 - _time0 < _minTimeInterval) { + _time1 = Math.min(_time1_, _time0 + _minTimeInterval); + } + } + + /** + * @param traces + * @param start + * @param end + */ + void updateInternalData(ITimeGraphEntry[] traces, long start, long end) { + if (null == traces) { + traces = new ITimeGraphEntry[0]; + } + if ((start == 0 && end == 0) || start < 0 || end < 0) { + // Start and end time are unspecified and need to be determined from + // individual processes + setTimeRange(traces); + } else { + _beginTime = start; + _endTime = end; + } + + refreshAllData(traces); + } + + /** + * @param traces + */ + private void refreshAllData(ITimeGraphEntry[] traces) { + setTimeBounds(); + if (_selectedTime < _beginTime) { + _selectedTime = _beginTime; + } else if (_selectedTime > _endTime) { + _selectedTime = _endTime; + } + _stateCtrl.refreshData(traces); + _timeScaleCtrl.redraw(); + adjustVerticalScrollBar(); + } + + /** + * Callback for when this view is focused + */ + public void setFocus() { + if (null != _stateCtrl) { + _stateCtrl.setFocus(); + } + } + + /** + * Get the current focus status of this view. + * + * @return If the view is currently focused, or not + */ + public boolean isInFocus() { + return _stateCtrl.isInFocus(); + } + + /** + * Get the view's current selection + * + * @return The entry that is selected + */ + public ITimeGraphEntry getSelection() { + return _stateCtrl.getSelectedTrace(); + } + + /** + * Get the index of the current selection + * + * @return The index + */ + public int getSelectionIndex() { + return _stateCtrl.getSelectedIndex(); + } + + @Override + public long getTime0() { + return _time0; + } + + @Override + public long getTime1() { + return _time1; + } + + @Override + public long getMinTimeInterval() { + return _minTimeInterval; + } + + @Override + public int getNameSpace() { + return _nameWidth; + } + + @Override + public void setNameSpace(int width) { + _nameWidth = width; + width = _stateCtrl.getClientArea().width; + if (_nameWidth > width - 6) { + _nameWidth = width - 6; + } + if (_nameWidth < 6) { + _nameWidth = 6; + } + _stateCtrl.adjustScrolls(); + _stateCtrl.redraw(); + _timeScaleCtrl.redraw(); + } + + @Override + public int getTimeSpace() { + int w = _stateCtrl.getClientArea().width; + return w - _nameWidth; + } + + @Override + public long getSelectedTime() { + return _selectedTime; + } + + @Override + public long getBeginTime() { + return _beginTime; + } + + @Override + public long getEndTime() { + return _endTime; + } + + @Override + public long getMaxTime() { + return _time1_; + } + + @Override + public long getMinTime() { + return _time0_; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider + * #setStartFinishTimeNotify(long, long) + */ + @Override + public void setStartFinishTimeNotify(long time0, long time1) { + setStartFinishTime(time0, time1); + notifyRangeListeners(time0, time1); + } + + + /* (non-Javadoc) + * @see org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider#notifyStartFinishTime() + */ + @Override + public void notifyStartFinishTime() { + notifyRangeListeners(_time0, _time1); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider + * #setStartFinishTime(long, long) + */ + @Override + public void setStartFinishTime(long time0, long time1) { + _time0 = time0; + if (_time0 < _time0_) { + _time0 = _time0_; + } + if (_time0 > _time1_) { + _time0 = _time1_; + } + _time1 = time1; + if (_time1 < _time0_) { + _time1 = _time0_; + } + if (_time1 > _time1_) { + _time1 = _time1_; + } + if (_time1 - _time0 < _minTimeInterval) { + _time1 = Math.min(_time1_, _time0 + _minTimeInterval); + } + _timeRangeFixed = true; + _stateCtrl.adjustScrolls(); + _stateCtrl.redraw(); + _timeScaleCtrl.redraw(); + } + + /** + * Set the time bounds to the provided values + * + * @param beginTime + * The start time of the window + * @param endTime + * The end time + */ + public void setTimeBounds(long beginTime, long endTime) { + _beginTime = beginTime; + _endTime = endTime; + _time0_ = beginTime; + _time1_ = endTime; + _stateCtrl.adjustScrolls(); + } + + @Override + public void resetStartFinishTime() { + setStartFinishTimeNotify(_time0_, _time1_); + _timeRangeFixed = false; + } + + @Override + public void setSelectedTimeNotify(long time, boolean ensureVisible) { + setSelectedTimeInt(time, ensureVisible, true); + } + + @Override + public void setSelectedTime(long time, boolean ensureVisible) { + setSelectedTimeInt(time, ensureVisible, false); + } + + private void setSelectedTimeInt(long time, boolean ensureVisible, boolean doNotify) { + long time0 = _time0; + long time1 = _time1; + if (ensureVisible) { + long timeSpace = (long) ((_time1 - _time0) * .02); + long timeMid = (long) ((_time1 - _time0) * .5); + if (time < _time0 + timeSpace) { + long dt = _time0 - time + timeMid; + _time0 -= dt; + _time1 -= dt; + } else if (time > _time1 - timeSpace) { + long dt = time - _time1 + timeMid; + _time0 += dt; + _time1 += dt; + } + if (_time0 < _time0_) { + _time1 = Math.min(_time1_, _time1 + (_time0_ - _time0)); + _time0 = _time0_; + } else if (_time1 > _time1_) { + _time0 = Math.max(_time0_, _time0 - (_time1 - _time1_)); + _time1 = _time1_; + } + } + if (_time1 - _time0 < _minTimeInterval) { + _time1 = Math.min(_time1_, _time0 + _minTimeInterval); + } + _stateCtrl.adjustScrolls(); + _stateCtrl.redraw(); + _timeScaleCtrl.redraw(); + + + boolean notifySelectedTime = (time != _selectedTime); + _selectedTime = time; + + if (doNotify && ((time0 != _time0) || (time1 != _time1))) { + notifyRangeListeners(_time0, _time1); + } + + if (doNotify && notifySelectedTime) { + notifyTimeListeners(_selectedTime); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + if (_selectedEntry != getSelection()) { + _selectedEntry = getSelection(); + notifySelectionListeners(_selectedEntry); + } + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (_selectedEntry != getSelection()) { + _selectedEntry = getSelection(); + notifySelectionListeners(_selectedEntry); + } + } + + /** + * Callback for when the next event is selected + */ + public void selectNextEvent() { + _stateCtrl.selectNextEvent(); + adjustVerticalScrollBar(); + } + + /** + * Callback for when the previous event is selected + */ + public void selectPrevEvent() { + _stateCtrl.selectPrevEvent(); + adjustVerticalScrollBar(); + } + + /** + * Callback for when the next item is selected + */ + public void selectNextItem() { + _stateCtrl.selectNextTrace(); + adjustVerticalScrollBar(); + } + + /** + * Callback for when the previous item is selected + */ + public void selectPrevItem() { + _stateCtrl.selectPrevTrace(); + adjustVerticalScrollBar(); + } + + /** + * Callback for the show legend action + */ + public void showLegend() { + if (_dataViewer == null || _dataViewer.isDisposed()) { + return; + } + + TimeGraphLegend.open(_dataViewer.getShell(), fTimeGraphProvider); + } + + /** + * Callback for the Zoom In action + */ + public void zoomIn() { + _stateCtrl.zoomIn(); + } + + /** + * Callback for the Zoom Out action + */ + public void zoomOut() { + _stateCtrl.zoomOut(); + } + + private String getPreferenceString(String string) { + return getViewTypeStr() + "." + string; //$NON-NLS-1$ + } + + /** + * Add a selection listener + * + * @param listener + * The listener to add + */ + public void addSelectionListener(ITimeGraphSelectionListener listener) { + fSelectionListeners.add(listener); + } + + /** + * Remove a selection listener + * + * @param listener + * The listener to remove + */ + public void removeSelectionListener(ITimeGraphSelectionListener listener) { + fSelectionListeners.remove(listener); + } + + private void notifySelectionListeners(ITimeGraphEntry selection) { + TimeGraphSelectionEvent event = new TimeGraphSelectionEvent(this, selection); + + for (ITimeGraphSelectionListener listener : fSelectionListeners) { + listener.selectionChanged(event); + } + } + + /** + * Add a time listener + * + * @param listener + * The listener to add + */ + public void addTimeListener(ITimeGraphTimeListener listener) { + fTimeListeners.add(listener); + } + + /** + * Remove a time listener + * + * @param listener + * The listener to remove + */ + public void removeTimeListener(ITimeGraphTimeListener listener) { + fTimeListeners.remove(listener); + } + + private void notifyTimeListeners(long time) { + TimeGraphTimeEvent event = new TimeGraphTimeEvent(this, time); + + for (ITimeGraphTimeListener listener : fTimeListeners) { + listener.timeSelected(event); + } + } + + /** + * Add a range listener + * + * @param listener + * The listener to add + */ + public void addRangeListener(ITimeGraphRangeListener listener) { + fRangeListeners.add(listener); + } + + /** + * Remove a range listener + * + * @param listener + * The listener to remove + */ + public void removeRangeListener(ITimeGraphRangeListener listener) { + fRangeListeners.remove(listener); + } + + private void notifyRangeListeners(long startTime, long endTime) { + // Check if the time has actually changed from last notification + if (startTime != _time0_extSynch || endTime != _time1_extSynch) { + // Notify Time Scale Selection Listeners + TimeGraphRangeUpdateEvent event = new TimeGraphRangeUpdateEvent(this, startTime, endTime); + + for (ITimeGraphRangeListener listener : fRangeListeners) { + listener.timeRangeUpdated(event); + } + + // update external synch timers + updateExtSynchTimers(); + } + } + + /** + * Callback to set a selected event in the view + * + * @param event + * The event that was selected + * @param source + * The source of this selection event + */ + public void setSelectedEvent(ITimeEvent event, Object source) { + if (event == null || source == this) { + return; + } + _selectedEntry = event.getEntry(); + _stateCtrl.selectItem(_selectedEntry, false); + + setSelectedTimeInt(event.getTime(), true, true); + adjustVerticalScrollBar(); + } + + /** + * Set the seeked time of a trace + * + * @param trace + * The trace that was seeked + * @param time + * The target time + * @param source + * The source of this seek event + */ + public void setSelectedTraceTime(ITimeGraphEntry trace, long time, Object source) { + if (trace == null || source == this) { + return; + } + _selectedEntry = trace; + _stateCtrl.selectItem(trace, false); + + setSelectedTimeInt(time, true, true); + } + + /** + * Callback for a trace selection + * + * @param trace + * The trace that was selected + */ + public void setSelection(ITimeGraphEntry trace) { + _selectedEntry = trace; + _stateCtrl.selectItem(trace, false); + adjustVerticalScrollBar(); + } + + /** + * Callback for a time window selection + * + * @param time0 + * Start time of the range + * @param time1 + * End time of the range + * @param source + * Source of the event + */ + public void setSelectVisTimeWindow(long time0, long time1, Object source) { + if (source == this) { + return; + } + + setStartFinishTime(time0, time1); + + // update notification time values since we are now in synch with the + // external application + updateExtSynchTimers(); + } + + /** + * update the cache timers used to identify the need to send a time window + * update to external registered listeners + */ + private void updateExtSynchTimers() { + // last time notification cache + _time0_extSynch = _time0; + _time1_extSynch = _time1; + } + + /** + * Set the calendar format + * + * @param toAbsoluteCaltime + * True for absolute time, false for relative + */ + public void setTimeCalendarFormat(boolean toAbsoluteCaltime) { + calendarTimeFormat = toAbsoluteCaltime; + } + + @Override + public boolean isCalendarFormat() { + return calendarTimeFormat; + } + + /** + * Retrieve the border width + * + * @return The width + */ + public int getBorderWidth() { + return borderWidth; + } + + /** + * Set the border width + * + * @param borderWidth + * The width + */ + public void setBorderWidth(int borderWidth) { + if (borderWidth > -1) { + this.borderWidth = borderWidth; + GridLayout gl = (GridLayout)_dataViewer.getLayout(); + gl.marginHeight = borderWidth; + } + } + + /** + * Retrieve the height of the header + * + * @return The height + */ + public int getHeaderHeight() { + return timeScaleHeight; + } + + /** + * Set the height of the header + * + * @param headerHeight + * The height to set + */ + public void setHeaderHeight(int headerHeight) { + if (headerHeight > -1) { + this.timeScaleHeight = headerHeight; + _timeScaleCtrl.setHeight(headerHeight); + } + } + + /** + * Retrieve the height of an item row + * + * @return The height + */ + public int getItemHeight() { + if (_stateCtrl != null) { + return _stateCtrl.getItemHeight(); + } + return 0; + } + + /** + * Set the height of an item row + * + * @param rowHeight + * The height to set + */ + public void setItemHeight(int rowHeight) { + if (_stateCtrl != null) { + _stateCtrl.setItemHeight(rowHeight); + } + } + + /** + * Set the minimum item width + * + * @param width + * The min width + */ + public void setMinimumItemWidth(int width) { + if (_stateCtrl != null) { + _stateCtrl.setMinimumItemWidth(width); + } + } + + /** + * Set the width for the name column + * + * @param width The width + */ + public void setNameWidthPref(int width) { + _nameWidthPref = width; + if (width == 0) { + _minNameWidth = 0; + _nameWidth = 0; + } + } + + /** + * Retrieve the configure width for the name column + * + * @param width + * Unused? + * @return The width + */ + public int getNameWidthPref(int width) { + return _nameWidthPref; + } + + /** + * Returns the primary control associated with this viewer. + * + * @return the SWT control which displays this viewer's content + */ + public Control getControl() { + return _dataViewer; + } + + /** + * Returns the time graph control associated with this viewer. + * + * @return the time graph control + */ + TimeGraphControl getTimeGraphControl() { + return _stateCtrl; + } + + /** + * Returns the time graph scale associated with this viewer. + * + * @return the time graph scale + */ + TimeGraphScale getTimeGraphScale() { + return _timeScaleCtrl; + } + + /** + * Get the selection provider + * + * @return the selection provider + */ + public ISelectionProvider getSelectionProvider() { + return _stateCtrl; + } + + /** + * Wait for the cursor + * + * @param waitInd + * Wait indefinitely? + */ + public void waitCursor(boolean waitInd) { + _stateCtrl.waitCursor(waitInd); + } + + /** + * Get the horizontal scroll bar object + * + * @return The scroll bar + */ + public ScrollBar getHorizontalBar() { + return _stateCtrl.getHorizontalBar(); + } + + /** + * Get the vertical scroll bar object + * + * @return The scroll bar + */ + public Slider getVerticalBar() { + return _verticalScrollBar; + } + + /** + * Set the given index as the top one + * + * @param index + * The index that will go to the top + */ + public void setTopIndex(int index) { + _stateCtrl.setTopIndex(index); + adjustVerticalScrollBar(); + } + + /** + * Retrieve the current top index + * + * @return The top index + */ + public int getTopIndex() { + return _stateCtrl.getTopIndex(); + } + + /** + * Set the expanded state of an entry + * + * @param entry + * The entry to expand/collapse + * @param expanded + * True for expanded, false for collapsed + */ + public void setExpandedState(ITimeGraphEntry entry, boolean expanded) { + _stateCtrl.setExpandedState(entry, expanded); + adjustVerticalScrollBar(); + } + + /** + * Collapses all nodes of the viewer's tree, starting with the root. + * + * @since 2.0 + */ + public void collapseAll() { + _stateCtrl.collapseAll(); + adjustVerticalScrollBar(); + } + + /** + * Expands all nodes of the viewer's tree, starting with the root. + * + * @since 2.0 + */ + public void expandAll() { + _stateCtrl.expandAll(); + adjustVerticalScrollBar(); + } + + /** + * Get the number of sub-elements when expanded + * + * @return The element count + */ + public int getExpandedElementCount() { + return _stateCtrl.getExpandedElementCount(); + } + + /** + * Get the sub-elements + * + * @return The array of entries that are below this one + */ + public ITimeGraphEntry[] getExpandedElements() { + return _stateCtrl.getExpandedElements(); + } + + /** + * Add a tree listener + * + * @param listener + * The listener to add + */ + public void addTreeListener(ITimeGraphTreeListener listener) { + _stateCtrl.addTreeListener(listener); + } + + /** + * Remove a tree listener + * + * @param listener + * The listener to remove + */ + public void removeTreeListener(ITimeGraphTreeListener listener) { + _stateCtrl.removeTreeListener(listener); + } + + /** + * Get the reset scale action. + * + * @return The Action object + */ + public Action getResetScaleAction() { + if (resetScale == null) { + // resetScale + resetScale = new Action() { + @Override + public void run() { + resetStartFinishTime(); + } + }; + resetScale.setText(Messages.TmfTimeGraphViewer_ResetScaleActionNameText); + resetScale.setToolTipText(Messages.TmfTimeGraphViewer_ResetScaleActionToolTipText); + resetScale.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_HOME_MENU)); + } + return resetScale; + } + + /** + * Get the show legend action. + * + * @return The Action object + */ + public Action getShowLegendAction() { + if (showLegendAction == null) { + // showLegend + showLegendAction = new Action() { + @Override + public void run() { + showLegend(); + } + }; + showLegendAction.setText(Messages.TmfTimeGraphViewer_LegendActionNameText); + showLegendAction.setToolTipText(Messages.TmfTimeGraphViewer_LegendActionToolTipText); + showLegendAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LEGEND)); + } + + return showLegendAction; + } + + /** + * Get the the next event action. + * + * @return The action object + */ + public Action getNextEventAction() { + if (nextEventAction == null) { + nextEventAction = new Action() { + @Override + public void run() { + selectNextEvent(); + } + }; + + nextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText); + nextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText); + nextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT)); + } + + return nextEventAction; + } + + /** + * Get the previous event action. + * + * @return The Action object + */ + public Action getPreviousEventAction() { + if (prevEventAction == null) { + prevEventAction = new Action() { + @Override + public void run() { + selectPrevEvent(); + } + }; + + prevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText); + prevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText); + prevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT)); + } + + return prevEventAction; + } + + /** + * Get the next item action. + * + * @return The Action object + */ + public Action getNextItemAction() { + if (nextItemAction == null) { + + nextItemAction = new Action() { + @Override + public void run() { + selectNextItem(); + } + }; + nextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText); + nextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText); + nextItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_ITEM)); + } + return nextItemAction; + } + + /** + * Get the previous item action. + * + * @return The Action object + */ + public Action getPreviousItemAction() { + if (previousItemAction == null) { + + previousItemAction = new Action() { + @Override + public void run() { + selectPrevItem(); + } + }; + previousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText); + previousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText); + previousItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_ITEM)); + } + return previousItemAction; + } + + /** + * Get the zoom in action + * + * @return The Action object + */ + public Action getZoomInAction() { + if (zoomInAction == null) { + zoomInAction = new Action() { + @Override + public void run() { + zoomIn(); + } + }; + zoomInAction.setText(Messages.TmfTimeGraphViewer_ZoomInActionNameText); + zoomInAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomInActionToolTipText); + zoomInAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_IN_MENU)); + } + return zoomInAction; + } + + /** + * Get the zoom out action + * + * @return The Action object + */ + public Action getZoomOutAction() { + if (zoomOutAction == null) { + zoomOutAction = new Action() { + @Override + public void run() { + zoomOut(); + } + }; + zoomOutAction.setText(Messages.TmfTimeGraphViewer_ZoomOutActionNameText); + zoomOutAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomOutActionToolTipText); + zoomOutAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_OUT_MENU)); + } + return zoomOutAction; + } + + + private void adjustVerticalScrollBar() { + int topIndex = _stateCtrl.getTopIndex(); + int countPerPage = _stateCtrl.countPerPage(); + int expandedElementCount = _stateCtrl.getExpandedElementCount(); + if (topIndex + countPerPage > expandedElementCount) { + _stateCtrl.setTopIndex(Math.max(0, expandedElementCount - countPerPage)); + } + + int selection = _stateCtrl.getTopIndex(); + int min = 0; + int max = Math.max(1, expandedElementCount - 1); + int thumb = Math.min(max, Math.max(1, countPerPage - 1)); + int increment = 1; + int pageIncrement = Math.max(1, countPerPage); + _verticalScrollBar.setValues(selection, min, max, thumb, increment, pageIncrement); + } + + + +} diff --git a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/widgets/TimeGraphControl.java b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/widgets/TimeGraphControl.java index 8bce4c89b2..202d5bb1bc 100644 --- a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/widgets/TimeGraphControl.java +++ b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/widgets/TimeGraphControl.java @@ -1,1891 +1,1917 @@ -/***************************************************************************** - * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 Ericsson. - * 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 - * - * Contributors: - * Intel Corporation - Initial API and implementation - * Ruslan A. Scherbakov, Intel - Initial API and implementation - * Alvaro Sanchez-Leon - Updated for TMF - * Patrick Tasse - Refactoring - * - *****************************************************************************/ - -package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Vector; - -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphTreeListener; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.StateItem; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent; -import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.ControlListener; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.MouseMoveListener; -import org.eclipse.swt.events.MouseTrackListener; -import org.eclipse.swt.events.MouseWheelListener; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Cursor; -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.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.ScrollBar; - -/** - * Time graph control implementation - * - * @version 1.0 - * @author Alvaro Sanchez-Leon - * @author Patrick Tasse - */ -public class TimeGraphControl extends TimeGraphBaseControl implements FocusListener, KeyListener, MouseMoveListener, MouseListener, MouseWheelListener, ControlListener, SelectionListener, MouseTrackListener, TraverseListener, ISelectionProvider { - - private static final int DRAG_NONE = 0; - private static final int DRAG_TRACE_ITEM = 1; - private static final int DRAG_SPLIT_LINE = 2; - public static final boolean DEFAULT_DRAW_THREAD_JOIN = true; - public static final boolean DEFAULT_DRAW_THREAD_WAIT = true; - public static final boolean DEFAULT_DRAW_THREAD_RELEASE = true; - public static final int H_SCROLLBAR_MAX = Integer.MAX_VALUE - 1; - private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider - - private static final double zoomCoeff = 1.5; - - private ITimeDataProvider _timeProvider; - private boolean _isInFocus = false; - private boolean _isDragCursor3 = false; - private boolean _isWaitCursor = true; - private boolean _mouseOverSplitLine = false; - private int _itemHeight = CUSTOM_ITEM_HEIGHT; - private int _minimumItemWidth = 0; - private int _topIndex = 0; - private int _dragState = DRAG_NONE; - private int _dragX0 = 0; - private int _dragX = 0; - private int _idealNameSpace = 0; - // private double _timeStep = 10000000; - private long _time0bak; - private long _time1bak; - private ITimeGraphPresentationProvider fTimeGraphProvider = null; - private ItemData _data = null; - private List _selectionListeners; - private final List _selectionChangedListeners = new ArrayList(); - private final List _treeListeners = new ArrayList(); - private final Cursor _dragCursor3; - private final Cursor _WaitCursor; - - // Vertical formatting formatting for the state control view - private final boolean _visibleVerticalScroll = true; - private int _borderWidth = 0; - private int _headerHeight = 0; - - private Listener mouseScrollFilterListener; - - protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources()); - protected Color[] fEventColorMap = null; - - private MouseScrollNotifier fMouseScrollNotifier; - private final Object fMouseScrollNotifierLock = new Object(); - private class MouseScrollNotifier extends Thread { - private final static long DELAY = 400L; - private final static long POLLING_INTERVAL = 10L; - private long fLastScrollTime = Long.MAX_VALUE; - - @Override - public void run() { - while ((System.currentTimeMillis() - fLastScrollTime) < DELAY) { - try { - Thread.sleep(POLLING_INTERVAL); - } catch (Exception e) { - return; - } - } - if (!isInterrupted()) { - Display.getDefault().asyncExec(new Runnable() { - @Override - public void run() { - if (isDisposed()) { - return; - } - _timeProvider.notifyStartFinishTime(); - } - }); - } - synchronized (fMouseScrollNotifierLock) { - fMouseScrollNotifier = null; - } - } - - public void mouseScrolled() { - fLastScrollTime = System.currentTimeMillis(); - } - } - - /** - * Standard constructor - * - * @param parent - * The parent composite object - * @param colors - * The color scheme to use - */ - public TimeGraphControl(Composite parent, TimeGraphColorScheme colors) { - - super(parent, colors, SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.DOUBLE_BUFFERED); - - _data = new ItemData(); - - addFocusListener(this); - addMouseListener(this); - addMouseMoveListener(this); - addMouseTrackListener(this); - addMouseWheelListener(this); - addTraverseListener(this); - addKeyListener(this); - addControlListener(this); - ScrollBar scrollHor = getHorizontalBar(); - - if (scrollHor != null) { - scrollHor.addSelectionListener(this); - } - - _dragCursor3 = new Cursor(super.getDisplay(), SWT.CURSOR_SIZEWE); - _WaitCursor = new Cursor(super.getDisplay(), SWT.CURSOR_WAIT); - } - - @Override - public void dispose() { - super.dispose(); - _dragCursor3.dispose(); - _WaitCursor.dispose(); - fResourceManager.dispose(); - } - - /** - * Sets the timegraph provider used by this timegraph viewer. - * - * @param timeGraphProvider the timegraph provider - */ - public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { - fTimeGraphProvider = timeGraphProvider; - _data.provider = timeGraphProvider; - - if (fEventColorMap != null) { - for (Color color : fEventColorMap) { - fResourceManager.destroyColor(color.getRGB()); - } - } - StateItem[] stateItems = fTimeGraphProvider.getStateTable(); - if (stateItems != null) { - fEventColorMap = new Color[stateItems.length]; - for (int i = 0; i < stateItems.length; i++) { - fEventColorMap[i] = fResourceManager.createColor(stateItems[i].getStateColor()); - } - } else { - fEventColorMap = new Color[] { }; - } - } - - /** - * Assign the given time provider - * - * @param timeProvider - * The time provider - */ - public void setTimeProvider(ITimeDataProvider timeProvider) { - _timeProvider = timeProvider; - adjustScrolls(); - redraw(); - } - - /** - * Add a selection listener - * - * @param listener - * The listener to add - */ - public void addSelectionListener(SelectionListener listener) { - if (listener == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - } - if (null == _selectionListeners) { - _selectionListeners = new ArrayList(); - } - _selectionListeners.add(listener); - } - - /** - * Remove a selection listener - * - * @param listener - * The listener to remove - */ - public void removeSelectionListener(SelectionListener listener) { - if (null != _selectionListeners) { - _selectionListeners.remove(listener); - } - } - - /** - * Selection changed callback - */ - public void fireSelectionChanged() { - if (null != _selectionListeners) { - Iterator it = _selectionListeners.iterator(); - while (it.hasNext()) { - SelectionListener listener = it.next(); - listener.widgetSelected(null); - } - } - } - - /** - * Default selection callback - */ - public void fireDefaultSelection() { - if (null != _selectionListeners) { - Iterator it = _selectionListeners.iterator(); - while (it.hasNext()) { - SelectionListener listener = it.next(); - listener.widgetDefaultSelected(null); - } - } - } - - /** - * Get the traces in the model - * - * @return The array of traces - */ - public ITimeGraphEntry[] getTraces() { - return _data.getTraces(); - } - - /** - * Get the on/off trace filters - * - * @return The array of filters - */ - public boolean[] getTraceFilter() { - return _data.getTraceFilter(); - } - - /** - * Refresh the data for the thing - */ - public void refreshData() { - _data.refreshData(); - adjustScrolls(); - redraw(); - } - - /** - * Refresh data for the given traces - * - * @param traces - * The traces to refresh - */ - public void refreshData(ITimeGraphEntry[] traces) { - _data.refreshData(traces); - adjustScrolls(); - redraw(); - } - - /** - * Adjust the scoll bars - */ - public void adjustScrolls() { - if (null == _timeProvider) { - getHorizontalBar().setValues(0, 1, 1, 1, 1, 1); - return; - } - - // HORIZONTAL BAR - // Visible window - long time0 = _timeProvider.getTime0(); - long time1 = _timeProvider.getTime1(); - // Time boundaries - long timeMin = _timeProvider.getMinTime(); - long timeMax = _timeProvider.getMaxTime(); - - long delta = timeMax - timeMin; - - int timePos = 0; - int thumb = H_SCROLLBAR_MAX; - - if (delta != 0) { - // Thumb size (page size) - thumb = Math.max(1, (int) (H_SCROLLBAR_MAX * ((double) (time1 - time0) / delta))); - // At the beginning of visible window - timePos = (int) (H_SCROLLBAR_MAX * ((double) (time0 - timeMin) / delta)); - } - - // position, minimum, maximum, thumb size, increment (half page)t, page - // increment size (full page) - getHorizontalBar().setValues(timePos, 0, H_SCROLLBAR_MAX, thumb, Math.max(1, thumb / 2), Math.max(2, thumb)); - } - - boolean ensureVisibleItem(int idx, boolean redraw) { - boolean changed = false; - if (idx < 0) { - for (idx = 0; idx < _data._expandedItems.length; idx++) { - if (_data._expandedItems[idx]._selected) { - break; - } - } - } - if (idx >= _data._expandedItems.length) { - return changed; - } - if (idx < _topIndex) { - setTopIndex(idx); - //FIXME:getVerticalBar().setSelection(_topItem); - if (redraw) { - redraw(); - } - changed = true; - } else { - int page = countPerPage(); - if (idx >= _topIndex + page) { - setTopIndex(idx - page + 1); - //FIXME:getVerticalBar().setSelection(_topItem); - if (redraw) { - redraw(); - } - changed = true; - } - } - return changed; - } - - /** - * Assign the given index as the top one - * - * @param idx - * The index - */ - public void setTopIndex(int idx) { - idx = Math.min(idx, _data._expandedItems.length - countPerPage()); - idx = Math.max(0, idx); - _topIndex = idx; - redraw(); - } - - /** - * Set the expanded state of a given entry - * - * @param entry - * The entry - * @param expanded - * True if expanded, false if collapsed - */ - public void setExpandedState(ITimeGraphEntry entry, boolean expanded) { - Item item = _data.findItem(entry); - if (item != null && item._expanded != expanded) { - item._expanded = expanded; - _data.updateExpandedItems(); - redraw(); - } - } - - /** - * Add a tree listener - * - * @param listener - * The listener to add - */ - public void addTreeListener(ITimeGraphTreeListener listener) { - if (!_treeListeners.contains(listener)) { - _treeListeners.add(listener); - } - } - - /** - * Remove a tree listener - * - * @param listener - * The listener to remove - */ - public void removeTreeListener(ITimeGraphTreeListener listener) { - if (_treeListeners.contains(listener)) { - _treeListeners.remove(listener); - } - } - - /** - * Tree event callback - * - * @param entry - * The affected entry - * @param expanded - * The expanded state (true for expanded, false for collapsed) - */ - public void fireTreeEvent(ITimeGraphEntry entry, boolean expanded) { - TimeGraphTreeExpansionEvent event = new TimeGraphTreeExpansionEvent(this, entry); - for (ITimeGraphTreeListener listener : _treeListeners) { - if (expanded) { - listener.treeExpanded(event); - } else { - listener.treeCollapsed(event); - } - } - } - - @Override - public ISelection getSelection() { - TimeGraphSelection sel = new TimeGraphSelection(); - ITimeGraphEntry trace = getSelectedTrace(); - if (null != trace && null != _timeProvider) { - long selectedTime = _timeProvider.getSelectedTime(); - ITimeEvent event = Utils.findEvent(trace, selectedTime, 0); - if (event != null) { - sel.add(event); - } else { - sel.add(trace); - } - } - return sel; - } - - /** - * Get the selection object - * - * @return The selection - */ - public ISelection getSelectionTrace() { - TimeGraphSelection sel = new TimeGraphSelection(); - ITimeGraphEntry trace = getSelectedTrace(); - if (null != trace) { - sel.add(trace); - } - return sel; - } - - /** - * Enable/disable one of the traces in the model - * - * @param n - * 1 to enable it, -1 to disable. The method returns immediately - * if another value is used. - */ - public void selectTrace(int n) { - if ((n != 1) && (n != -1)) { - return; - } - - boolean changed = false; - int lastSelection = -1; - for (int i = 0; i < _data._expandedItems.length; i++) { - Item item = _data._expandedItems[i]; - if (item._selected) { - lastSelection = i; - if ((1 == n) && (i < _data._expandedItems.length - 1)) { - item._selected = false; - item = _data._expandedItems[i + 1]; - item._selected = true; - changed = true; - } else if ((-1 == n) && (i > 0)) { - item._selected = false; - item = _data._expandedItems[i - 1]; - item._selected = true; - changed = true; - } - break; - } - } - - if (lastSelection < 0 && _data._expandedItems.length > 0) { - Item item = _data._expandedItems[0]; - item._selected = true; - changed = true; - } - - if (changed) { - ensureVisibleItem(-1, false); - redraw(); - fireSelectionChanged(); - } - } - - /** - * Select an event - * - * @param n - * 1 for next event, -1 for previous event - */ - public void selectEvent(int n) { - if (null == _timeProvider) { - return; - } - ITimeGraphEntry trace = getSelectedTrace(); - if (trace == null) { - return; - } - long selectedTime = _timeProvider.getSelectedTime(); - long endTime = _timeProvider.getEndTime(); - ITimeEvent nextEvent; - if (-1 == n && selectedTime > endTime) { - nextEvent = Utils.findEvent(trace, selectedTime, 0); - } else { - nextEvent = Utils.findEvent(trace, selectedTime, n); - } - if (null == nextEvent && -1 == n) { - nextEvent = Utils.getFirstEvent(trace); - } - if (null != nextEvent) { - long nextTime = nextEvent.getTime(); - // If last event detected e.g. going back or not moving to a next - // event - if (nextTime <= selectedTime && n == 1) { - // Select to the end of this last event - nextTime = nextEvent.getTime() + nextEvent.getDuration(); - // but not beyond the end of the trace - if (nextTime > endTime) { - nextTime = endTime; - } - } else if (n == -1) { - // for previous event go to its end time unless we were already there - if (nextEvent.getTime() + nextEvent.getDuration() < selectedTime) { - nextTime = nextEvent.getTime() + nextEvent.getDuration(); - } - } - _timeProvider.setSelectedTimeNotify(nextTime, true); - fireSelectionChanged(); - } else if (1 == n) { - _timeProvider.setSelectedTimeNotify(endTime, true); - fireSelectionChanged(); - } - } - - /** - * Select the next event - */ - public void selectNextEvent() { - selectEvent(1); - // Notify if visible time window has been adjusted - _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1()); - } - - /** - * Select the previous event - */ - public void selectPrevEvent() { - selectEvent(-1); - // Notify if visible time window has been adjusted - _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1()); - } - - /** - * Select the next trace - */ - public void selectNextTrace() { - selectTrace(1); - } - - /** - * Select the previous trace - */ - public void selectPrevTrace() { - selectTrace(-1); - } - - /** - * Zoom based on mouse cursor location with mouse scrolling - * - * @param zoomIn true to zoom in, false to zoom out - */ - public void zoom(boolean zoomIn) { - int globalX = getDisplay().getCursorLocation().x; - Point p = toControl(globalX, 0); - int nameSpace = _timeProvider.getNameSpace(); - int timeSpace = _timeProvider.getTimeSpace(); - int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x)); - long time0 = _timeProvider.getTime0(); - long time1 = _timeProvider.getTime1(); - long interval = time1 - time0; - if (interval == 0) { - interval = 1; - } // to allow getting out of single point interval - long newInterval; - if (zoomIn) { - newInterval = Math.max(Math.round(interval * 0.8), _timeProvider.getMinTimeInterval()); - } else { - newInterval = (long) Math.ceil(interval * 1.25); - } - long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval)); - long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval); - long newTime1 = newTime0 + newInterval; - _timeProvider.setStartFinishTime(newTime0, newTime1); - synchronized (fMouseScrollNotifierLock) { - if (fMouseScrollNotifier == null) { - fMouseScrollNotifier = new MouseScrollNotifier(); - fMouseScrollNotifier.start(); - } - fMouseScrollNotifier.mouseScrolled(); - } - } - - /** - * zoom in using single click - */ - public void zoomIn() { - long _time0 = _timeProvider.getTime0(); - long _time1 = _timeProvider.getTime1(); - long _range = _time1 - _time0; - long selTime = _timeProvider.getSelectedTime(); - if (selTime <= _time0 || selTime >= _time1) { - selTime = (_time0 + _time1) / 2; - } - long time0 = selTime - (long) ((selTime - _time0) / zoomCoeff); - long time1 = selTime + (long) ((_time1 - selTime) / zoomCoeff); - - long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0); - - // Trace.debug("selTime:" + selTime + " time0:" + time0 + " time1:" - // + time1 + " inaccuracy:" + inaccuracy); - - if (inaccuracy > 0 && inaccuracy < 100) { - _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime()); - return; - } - - long m = _timeProvider.getMinTimeInterval(); - if ((time1 - time0) < m) { - time0 = selTime - (selTime - _time0) * m / _range; - time1 = time0 + m; - } - - _timeProvider.setStartFinishTimeNotify(time0, time1); - } - - /** - * zoom out using single click - */ - public void zoomOut() { - long _time0 = _timeProvider.getTime0(); - long _time1 = _timeProvider.getTime1(); - long selTime = _timeProvider.getSelectedTime(); - if (selTime <= _time0 || selTime >= _time1) { - selTime = (_time0 + _time1) / 2; - } - long time0 = (long) (selTime - (selTime - _time0) * zoomCoeff); - long time1 = (long) (selTime + (_time1 - selTime) * zoomCoeff); - - long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0); - if (inaccuracy > 0 && inaccuracy < 100) { - _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime()); - return; - } - - _timeProvider.setStartFinishTimeNotify(time0, time1); - } - - /** - * Return the currently selected trace - * - * @return The entry matching the trace - */ - public ITimeGraphEntry getSelectedTrace() { - ITimeGraphEntry trace = null; - int idx = getSelectedIndex(); - if (idx >= 0) { - trace = _data._expandedItems[idx]._trace; - } - return trace; - } - - /** - * Retrieve the index of the currently selected item - * - * @return The index - */ - public int getSelectedIndex() { - int idx = -1; - for (int i = 0; i < _data._expandedItems.length; i++) { - Item item = _data._expandedItems[i]; - if (item._selected) { - idx = i; - break; - } - } - return idx; - } - - boolean toggle(int idx) { - boolean toggled = false; - if (idx >= 0 && idx < _data._expandedItems.length) { - Item item = _data._expandedItems[idx]; - if (item._hasChildren) { - item._expanded = !item._expanded; - _data.updateExpandedItems(); - adjustScrolls(); - redraw(); - toggled = true; - fireTreeEvent(item._trace, item._expanded); - } - } - return toggled; - } - - int getItemIndexAtY(int y) { - if (y < 0) { - return -1; - } - if (_itemHeight == CUSTOM_ITEM_HEIGHT) { - int ySum = 0; - for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) { - ySum += _data._expandedItems[idx].itemHeight; - if (y < ySum) { - return idx; - } - } - return -1; - } - int idx = y / _itemHeight; - idx += _topIndex; - if (idx < _data._expandedItems.length) { - return idx; - } - return -1; - } - - boolean isOverSplitLine(int x) { - if (x < 0 || null == _timeProvider) { - return false; - } - int w = 4; - int nameWidth = _timeProvider.getNameSpace(); - if (x > nameWidth - w && x < nameWidth + w) { - return true; - } - return false; - } - - ITimeGraphEntry getEntry(Point pt) { - int idx = getItemIndexAtY(pt.y); - return idx >= 0 ? _data._expandedItems[idx]._trace : null; - } - - long getTimeAtX(int x) { - if (null == _timeProvider) { - return -1; - } - long hitTime = -1; - Point size = getCtrlSize(); - long time0 = _timeProvider.getTime0(); - long time1 = _timeProvider.getTime1(); - int nameWidth = _timeProvider.getNameSpace(); - x -= nameWidth; - int timeWidth = size.x - nameWidth - RIGHT_MARGIN; - if (x >= 0 && size.x >= nameWidth) { - if (time1 - time0 > timeWidth) { - // nanosecond smaller than one pixel: use the first integer nanosecond of this pixel's time range - hitTime = time0 + (long) Math.ceil((time1 - time0) * ((double) x / timeWidth)); - } else { - // nanosecond greater than one pixel: use the nanosecond that covers this pixel start position - hitTime = time0 + (long) Math.floor((time1 - time0) * ((double) x / timeWidth)); - } - } - return hitTime; - } - - void selectItem(int idx, boolean addSelection) { - boolean changed = false; - if (addSelection) { - if (idx >= 0 && idx < _data._expandedItems.length) { - Item item = _data._expandedItems[idx]; - changed = (item._selected == false); - item._selected = true; - } - } else { - for (int i = 0; i < _data._expandedItems.length; i++) { - Item item = _data._expandedItems[i]; - if ((i == idx && !item._selected) || (idx == -1 && item._selected)) { - changed = true; - } - item._selected = i == idx; - } - } - changed |= ensureVisibleItem(idx, true); - if (changed) { - redraw(); - } - } - - /** - * Callback for item selection - * - * @param trace - * The entry matching the trace - * @param addSelection - * If the selection is added or removed - */ - public void selectItem(ITimeGraphEntry trace, boolean addSelection) { - int idx = _data.findItemIndex(trace); - selectItem(idx, addSelection); - } - - /** - * Retrieve the number of entries shown per page. - * - * @return The count - */ - public int countPerPage() { - int height = getCtrlSize().y; - int count = 0; - if (_itemHeight == CUSTOM_ITEM_HEIGHT) { - int ySum = 0; - for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) { - ySum += _data._expandedItems[idx].itemHeight; - if (ySum >= height) { - return count; - } - count++; - } - for (int idx = _topIndex - 1; idx >= 0; idx--) { - ySum += _data._expandedItems[idx].itemHeight; - if (ySum >= height) { - return count; - } - count++; - } - return count; - } - if (height > 0) { - count = height / _itemHeight; - } - return count; - } - - /** - * Get the index of the top element - * - * @return The index - */ - public int getTopIndex() { - return _topIndex; - } - - /** - * Get the number of expanded items - * - * @return The count of expanded items - */ - public int getExpandedElementCount() { - return _data._expandedItems.length; - } - - /** - * Get an array of all expanded elements - * - * @return The expanded elements - */ - public ITimeGraphEntry[] getExpandedElements() { - ArrayList elements = new ArrayList(); - for (Item item : _data._expandedItems) { - elements.add(item._trace); - } - return elements.toArray(new ITimeGraphEntry[0]); - } - - Point getCtrlSize() { - Point size = getSize(); - if (getHorizontalBar().isVisible()) { - size.y -= getHorizontalBar().getSize().y; - } - return size; - } - - Rectangle getNameRect(Rectangle bound, int idx, int nameWidth) { - int x = bound.x; - int y = bound.y + (idx - _topIndex) * _itemHeight; - int width = nameWidth; - int height = _itemHeight; - if (_itemHeight == CUSTOM_ITEM_HEIGHT) { - int ySum = 0; - for (int i = _topIndex; i < idx; i++) { - ySum += _data._expandedItems[i].itemHeight; - } - y = bound.y + ySum; - height = _data._expandedItems[idx].itemHeight; - } - return new Rectangle(x, y, width, height); - } - - Rectangle getStatesRect(Rectangle bound, int idx, int nameWidth) { - int x = bound.x + nameWidth; - int y = bound.y + (idx - _topIndex) * _itemHeight; - int width = bound.width - x; - int height = _itemHeight; - if (_itemHeight == CUSTOM_ITEM_HEIGHT) { - int ySum = 0; - for (int i = _topIndex; i < idx; i++) { - ySum += _data._expandedItems[i].itemHeight; - } - y = bound.y + ySum; - height = _data._expandedItems[idx].itemHeight; - } - return new Rectangle(x, y, width, height); - } - - @Override - void paint(Rectangle bounds, PaintEvent e) { - GC gc = e.gc; - gc.setBackground(_colors.getColor(TimeGraphColorScheme.BACKGROUND)); - drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height); - - if (bounds.width < 2 || bounds.height < 2 || null == _timeProvider) { - return; - } - - _idealNameSpace = 0; - int nameSpace = _timeProvider.getNameSpace(); - - // draw empty name space background - gc.setBackground(_colors.getBkColor(false, false, true)); - drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height); - - drawItems(bounds, _timeProvider, _data._expandedItems, _topIndex, nameSpace, gc); - - // draw selected time - long time0 = _timeProvider.getTime0(); - long time1 = _timeProvider.getTime1(); - long selectedTime = _timeProvider.getSelectedTime(); - double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0); - int x = bounds.x + nameSpace + (int) ((selectedTime - time0) * pixelsPerNanoSec); - if (x >= nameSpace && x < bounds.x + bounds.width) { - gc.setForeground(_colors.getColor(TimeGraphColorScheme.SELECTED_TIME)); - gc.drawLine(x, bounds.y, x, bounds.y + bounds.height); - } - - // draw drag line, no line if name space is 0. - if (DRAG_SPLIT_LINE == _dragState) { - gc.setForeground(_colors.getColor(TimeGraphColorScheme.BLACK)); - gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1); - } else if (DRAG_NONE == _dragState && _mouseOverSplitLine && _timeProvider.getNameSpace() > 0) { - gc.setForeground(_colors.getColor(TimeGraphColorScheme.RED)); - gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1); - } - } - - /** - * Draw many items at once - * - * @param bounds - * The rectangle of the area - * @param timeProvider - * The time provider - * @param items - * The array items to draw - * @param topIndex - * The index of the first element to draw - * @param nameSpace - * The width reserved for the names - * @param gc - * Reference to the SWT GC object - */ - public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider, - Item[] items, int topIndex, int nameSpace, GC gc) { - for (int i = topIndex; i < items.length; i++) { - Item item = items[i]; - drawItem(item, bounds, timeProvider, i, nameSpace, gc); - } - fTimeGraphProvider.postDrawControl(bounds, gc); - } - - /** - * Draws the item - * - * @param item the item to draw - * @param bounds the container rectangle - * @param i the item index - * @param nameSpace the name space - * @param gc - */ - protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) { - ITimeGraphEntry entry = item._trace; - long time0 = timeProvider.getTime0(); - long time1 = timeProvider.getTime1(); - long selectedTime = timeProvider.getSelectedTime(); - - Rectangle nameRect = getNameRect(bounds, i, nameSpace); - if (nameRect.y >= bounds.y + bounds.height) { - return; - } - - if (! item._trace.hasTimeEvents()) { - Rectangle statesRect = getStatesRect(bounds, i, nameSpace); - nameRect.width += statesRect.width; - drawName(item, nameRect, gc); - } else { - drawName(item, nameRect, gc); - } - Rectangle rect = getStatesRect(bounds, i, nameSpace); - if (rect.isEmpty()) { - fTimeGraphProvider.postDrawEntry(entry, rect, gc); - return; - } - if (time1 <= time0) { - gc.setBackground(_colors.getBkColor(false, false, false)); - gc.fillRectangle(rect); - fTimeGraphProvider.postDrawEntry(entry, rect, gc); - return; - } - - // Initialize _rect1 to same values as enclosing rectangle rect - Rectangle stateRect = Utils.clone(rect); - boolean selected = item._selected; - // K pixels per second - double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0); - - if (item._trace.hasTimeEvents()) { - fillSpace(rect, gc, selected); - // Drawing rectangle is smaller than reserved space - stateRect.y += 3; - stateRect.height -= 6; - - long maxDuration = (timeProvider.getTimeSpace() == 0) ? Long.MAX_VALUE : 1 * (time1 - time0) / timeProvider.getTimeSpace(); - Iterator iterator = entry.getTimeEventsIterator(time0, time1, maxDuration); - - int lastX = -1; - while (iterator.hasNext()) { - ITimeEvent event = iterator.next(); - int x = rect.x + (int) ((event.getTime() - time0) * pixelsPerNanoSec); - int xEnd = rect.x + (int) ((event.getTime() + event.getDuration() - time0) * pixelsPerNanoSec); - if (x >= rect.x + rect.width || xEnd < rect.x) { - // event is out of bounds - continue; - } - xEnd = Math.min(rect.x + rect.width, xEnd); - stateRect.x = Math.max(rect.x, x); - stateRect.width = Math.max(0, xEnd - stateRect.x + 1); - if (stateRect.x == lastX) { - stateRect.width -= 1; - if (stateRect.width > 0) { - gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); - gc.drawPoint(stateRect.x, stateRect.y - 2); - stateRect.x += 1; - } - } else { - lastX = x; - } - boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration(); - drawState(_colors, event, stateRect, gc, selected, timeSelected); - } - } - fTimeGraphProvider.postDrawEntry(entry, rect, gc); - } - - protected void drawName(Item item, Rectangle bounds, GC gc) { - boolean hasTimeEvents = item._trace.hasTimeEvents(); - if (! hasTimeEvents) { - gc.setBackground(_colors.getBkColorGroup(item._selected, _isInFocus)); - gc.fillRectangle(bounds); - if (item._selected && _isInFocus) { - gc.setForeground(_colors.getBkColor(item._selected, _isInFocus, false)); - gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1); - } - } else { - gc.setBackground(_colors.getBkColor(item._selected, _isInFocus, true)); - gc.setForeground(_colors.getFgColor(item._selected, _isInFocus)); - gc.fillRectangle(bounds); - } - - // No name to be drawn - if (_timeProvider.getNameSpace() == 0) { - return; - } - - int leftMargin = MARGIN + item.level * EXPAND_SIZE; - if (item._hasChildren) { - gc.setForeground(_colors.getFgColorGroup(false, false)); - gc.setBackground(_colors.getBkColor(false, false, false)); - Rectangle rect = Utils.clone(bounds); - rect.x += leftMargin; - rect.y += (bounds.height - EXPAND_SIZE) / 2; - rect.width = EXPAND_SIZE; - rect.height = EXPAND_SIZE; - gc.fillRectangle(rect); - gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1); - int midy = rect.y + rect.height / 2; - gc.drawLine(rect.x + 2, midy, rect.x + rect.width - 3, midy); - if (!item._expanded) { - int midx = rect.x + rect.width / 2; - gc.drawLine(midx, rect.y + 2, midx, rect.y + rect.height - 3); - } - } - leftMargin += EXPAND_SIZE + MARGIN; - - Image img = fTimeGraphProvider.getItemImage(item._trace); - if (img != null) { - // draw icon - int imgHeight = img.getImageData().height; - int imgWidth = img.getImageData().width; - int x = leftMargin; - int y = bounds.y + (bounds.height - imgHeight) / 2; - gc.drawImage(img, x, y); - leftMargin += imgWidth + MARGIN; - } - String name = item._name; - Point size = gc.stringExtent(name); - if (_idealNameSpace < leftMargin + size.x + MARGIN) { - _idealNameSpace = leftMargin + size.x + MARGIN; - } - if (hasTimeEvents) { - // cut long string with "..." - int width = bounds.width - leftMargin; - int cuts = 0; - while (size.x > width && name.length() > 1) { - cuts++; - name = name.substring(0, name.length() - 1); - size = gc.stringExtent(name + "..."); //$NON-NLS-1$ - } - if (cuts > 0) { - name += "..."; //$NON-NLS-1$ - } - } - Rectangle rect = Utils.clone(bounds); - rect.x += leftMargin; - rect.width -= leftMargin; - // draw text - if (rect.width > 0) { - rect.y += (bounds.height - gc.stringExtent(name).y) / 2; - gc.setForeground(_colors.getFgColor(item._selected, _isInFocus)); - int textWidth = Utils.drawText(gc, name, rect, true); - leftMargin += textWidth + MARGIN; - rect.y -= 2; - - if (hasTimeEvents) { - // draw middle line - int x = bounds.x + leftMargin; - int width = bounds.width - x; - int midy = bounds.y + bounds.height / 2; - gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE)); - gc.drawLine(x, midy, x + width, midy); - } - } - } - - protected void drawState(TimeGraphColorScheme colors, ITimeEvent event, - Rectangle rect, GC gc, boolean selected, boolean timeSelected) { - - int colorIdx = fTimeGraphProvider.getStateTableIndex(event); - if (colorIdx < 0) { - return; - } - boolean visible = rect.width == 0 ? false : true; - - if (visible) { - Color stateColor = null; - if (colorIdx < fEventColorMap.length) { - stateColor = fEventColorMap[colorIdx]; - } else { - stateColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK); - } - - timeSelected = timeSelected && selected; - if (timeSelected) { - // modify the color? - } - // fill all rect area - gc.setBackground(stateColor); - gc.fillRectangle(rect); - // get the border color? - gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); - - // draw bounds - if (!timeSelected) { - // Draw the top and bottom borders i.e. no side borders - // top - gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y); - // bottom - gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1); - } - } else { - gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); - gc.drawPoint(rect.x, rect.y - 2); - /* - // selected rectangle area is not visible but can be represented - // with a broken vertical line of specified width. - int width = 1; - rect.width = width; - gc.setForeground(stateColor); - int s = gc.getLineStyle(); - int w = gc.getLineWidth(); - gc.setLineStyle(SWT.LINE_DOT); - gc.setLineWidth(width); - // Trace.debug("Rectangle not visible, drawing vertical line with: " - // + rect.x + "," + rect.y + "," + rect.x + "," + rect.y - // + rect.height); - gc.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1); - gc.setLineStyle(s); - gc.setLineWidth(w); - if (!timeSelected) { - gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); - gc.drawPoint(rect.x, rect.y); - gc.drawPoint(rect.x, rect.y + rect.height - 1); - } - */ - } - fTimeGraphProvider.postDrawEvent(event, rect, gc); - } - - protected void fillSpace(Rectangle rect, GC gc, boolean selected) { - gc.setBackground(_colors.getBkColor(selected, _isInFocus, false)); - gc.fillRectangle(rect); - // draw middle line - gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE)); - int midy = rect.y + rect.height / 2; - gc.drawLine(rect.x, midy, rect.x + rect.width, midy); - } - - @Override - public void keyTraversed(TraverseEvent e) { - if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) { - e.doit = true; - } - } - - @Override - public void keyPressed(KeyEvent e) { - int idx = -1; - if (_data._expandedItems.length == 0) { - return; - } - if (SWT.HOME == e.keyCode) { - idx = 0; - } else if (SWT.END == e.keyCode) { - idx = _data._expandedItems.length - 1; - } else if (SWT.ARROW_DOWN == e.keyCode) { - idx = getSelectedIndex(); - if (idx < 0) { - idx = 0; - } else if (idx < _data._expandedItems.length - 1) { - idx++; - } - } else if (SWT.ARROW_UP == e.keyCode) { - idx = getSelectedIndex(); - if (idx < 0) { - idx = 0; - } else if (idx > 0) { - idx--; - } - } else if (SWT.ARROW_LEFT == e.keyCode) { - selectPrevEvent(); - } else if (SWT.ARROW_RIGHT == e.keyCode) { - selectNextEvent(); - } else if (SWT.PAGE_DOWN == e.keyCode) { - int page = countPerPage(); - idx = getSelectedIndex(); - if (idx < 0) { - idx = 0; - } - idx += page; - if (idx >= _data._expandedItems.length) { - idx = _data._expandedItems.length - 1; - } - } else if (SWT.PAGE_UP == e.keyCode) { - int page = countPerPage(); - idx = getSelectedIndex(); - if (idx < 0) { - idx = 0; - } - idx -= page; - if (idx < 0) { - idx = 0; - } - } else if (SWT.CR == e.keyCode) { - idx = getSelectedIndex(); - if (idx >= 0) { - if (_data._expandedItems[idx]._hasChildren) { - toggle(idx); - } else { - fireDefaultSelection(); - } - } - idx = -1; - } - if (idx >= 0) { - selectItem(idx, false); - fireSelectionChanged(); - } - } - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void focusGained(FocusEvent e) { - _isInFocus = true; - if (mouseScrollFilterListener == null) { - mouseScrollFilterListener = new Listener() { - // This filter is used to prevent horizontal scrolling of the view - // when the mouse wheel is used to zoom - @Override - public void handleEvent(Event event) { - event.doit = false; - } - }; - getDisplay().addFilter(SWT.MouseWheel, mouseScrollFilterListener); - } - redraw(); - } - - @Override - public void focusLost(FocusEvent e) { - _isInFocus = false; - if (mouseScrollFilterListener != null) { - getDisplay().removeFilter(SWT.MouseWheel, mouseScrollFilterListener); - mouseScrollFilterListener = null; - } - if (DRAG_NONE != _dragState) { - setCapture(false); - _dragState = DRAG_NONE; - } - redraw(); - } - - /** - * @return If the current view is focused - */ - public boolean isInFocus() { - return _isInFocus; - } - - /** - * Provide the possibility to control the wait cursor externally e.g. data - * requests in progress - * - * @param waitInd Should we wait indefinitely? - */ - public void waitCursor(boolean waitInd) { - // Update cursor as indicated - if (waitInd) { - setCursor(_WaitCursor); - _isWaitCursor = true; - } else { - setCursor(null); - _isWaitCursor = false; - } - - // Get ready for next mouse move - _isDragCursor3 = false; - } - - /** - *

- * If the x, y position is over the vertical split line (name to time - * ranges), then change the cursor to a drag cursor to indicate the user the - * possibility of resizing - *

- * - * @param x - * @param y - */ - void updateCursor(int x, int y) { - // if Wait cursor not active, check for the need to change to a drag - // cursor - if (_isWaitCursor == false) { - boolean isSplitLine = isOverSplitLine(x); - // No dragcursor is name space is fixed to zero - if (isSplitLine && !_isDragCursor3 && _timeProvider.getNameSpace() > 0) { - setCursor(_dragCursor3); - _isDragCursor3 = true; - } else if (!isSplitLine && _isDragCursor3) { - setCursor(null); - _isDragCursor3 = false; - } - } - } - - @Override - public void mouseMove(MouseEvent e) { - if (null == _timeProvider) { - return; - } - Point size = getCtrlSize(); - if (DRAG_TRACE_ITEM == _dragState) { - int nameWidth = _timeProvider.getNameSpace(); - int x = e.x - nameWidth; - if (x > 0 && size.x > nameWidth && _dragX != x) { - _dragX = x; - double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (_time1bak - _time0bak); - long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((_dragX - _dragX0) / pixelsPerNanoSec)); - long time1 = _time1bak - timeDelta; - long maxTime = _timeProvider.getMaxTime(); - if (time1 > maxTime) { - time1 = maxTime; - } - long time0 = time1 - (_time1bak - _time0bak); - if (time0 < _timeProvider.getMinTime()) { - time0 = _timeProvider.getMinTime(); - time1 = time0 + (_time1bak - _time0bak); - } - _timeProvider.setStartFinishTime(time0, time1); - } - } else if (DRAG_SPLIT_LINE == _dragState) { - _dragX = e.x; - _timeProvider.setNameSpace(e.x); - } else if (DRAG_NONE == _dragState) { - boolean mouseOverSplitLine = isOverSplitLine(e.x); - if (_mouseOverSplitLine != mouseOverSplitLine) { - redraw(); - } - _mouseOverSplitLine = mouseOverSplitLine; - } - updateCursor(e.x, e.y); - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - if (null == _timeProvider) { - return; - } - if (1 == e.button) { - if (isOverSplitLine(e.x) && _timeProvider.getNameSpace() != 0) { - _timeProvider.setNameSpace(_idealNameSpace); - boolean mouseOverSplitLine = isOverSplitLine(e.x); - if (_mouseOverSplitLine != mouseOverSplitLine) { - redraw(); - } - _mouseOverSplitLine = mouseOverSplitLine; - return; - } - int idx = getItemIndexAtY(e.y); - if (idx >= 0) { - selectItem(idx, false); - fireDefaultSelection(); - } - } - } - - @Override - public void mouseDown(MouseEvent e) { - if (null == _timeProvider) { - return; - } - int idx; - if (1 == e.button) { - int nameSpace = _timeProvider.getNameSpace(); - if (nameSpace != 0) { - if (isOverSplitLine(e.x)) { - _dragState = DRAG_SPLIT_LINE; - _dragX = _dragX0 = e.x; - _time0bak = _timeProvider.getTime0(); - _time1bak = _timeProvider.getTime1(); - redraw(); - return; - } - } - - idx = getItemIndexAtY(e.y); - if (idx >= 0) { - Item item = _data._expandedItems[idx]; - if (item._hasChildren && e.x < nameSpace && e.x < MARGIN + (item.level + 1) * EXPAND_SIZE) { - toggle(idx); - } else { - long hitTime = getTimeAtX(e.x); - if (hitTime >= 0) { - // _timeProvider.setSelectedTimeInt(hitTime, false); - setCapture(true); - _dragState = DRAG_TRACE_ITEM; - _dragX = _dragX0 = e.x - nameSpace; - _time0bak = _timeProvider.getTime0(); - _time1bak = _timeProvider.getTime1(); - } - } - selectItem(idx, false); - fireSelectionChanged(); - } else { - selectItem(idx, false); // clear selection - redraw(); - fireSelectionChanged(); - } - } - } - - @Override - public void mouseUp(MouseEvent e) { - if (DRAG_NONE != _dragState) { - setCapture(false); - if (DRAG_TRACE_ITEM == _dragState) { - // Notify time provider to check the need for listener - // notification - _timeProvider.notifyStartFinishTime(); - if (_dragX == _dragX0) { // click without drag - long time = getTimeAtX(e.x); - _timeProvider.setSelectedTimeNotify(time, false); - } - } else if (DRAG_SPLIT_LINE == _dragState) { - redraw(); - } - _dragState = DRAG_NONE; - } - } - - @Override - public void mouseEnter(MouseEvent e) { - } - - @Override - public void mouseExit(MouseEvent e) { - if (_mouseOverSplitLine) { - _mouseOverSplitLine = false; - redraw(); - } - } - - @Override - public void mouseHover(MouseEvent e) { - } - - @Override - public void mouseScrolled(MouseEvent e) { - if ((mouseScrollFilterListener == null) || _dragState != DRAG_NONE) { - return; - } - boolean zoomScroll = false; - Point p = getParent().toControl(getDisplay().getCursorLocation()); - Point parentSize = getParent().getSize(); - if (p.x >= 0 && p.x < parentSize.x && p.y >= 0 && p.y < parentSize.y) { - // over the parent control - if (e.x > getCtrlSize().x) { - // over the horizontal scroll bar - zoomScroll = false; - } else if (e.y >= 0 && e.y < getCtrlSize().y && e.x < _timeProvider.getNameSpace()) { - // over the name space - zoomScroll = false; - } else { - zoomScroll = true; - } - } - if (zoomScroll && _timeProvider.getTime0() != _timeProvider.getTime1()) { - if (e.count > 0) { - zoom(true); - } else if (e.count < 0) { - zoom(false); - } - } else { - setTopIndex(getTopIndex() - e.count); - } - } - - @Override - public void controlMoved(ControlEvent e) { - } - - @Override - public void controlResized(ControlEvent e) { - adjustScrolls(); - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - - @Override - public void widgetSelected(SelectionEvent e) { - if (e.widget == getVerticalBar()) { - setTopIndex(getVerticalBar().getSelection()); - } else if (e.widget == getHorizontalBar() && null != _timeProvider) { - int start = getHorizontalBar().getSelection(); - long time0 = _timeProvider.getTime0(); - long time1 = _timeProvider.getTime1(); - long timeMin = _timeProvider.getMinTime(); - long timeMax = _timeProvider.getMaxTime(); - long delta = timeMax - timeMin; - - long range = time1 - time0; - // _timeRangeFixed = true; - time0 = timeMin + Math.round(delta * ((double) start / H_SCROLLBAR_MAX)); - time1 = time0 + range; - - // TODO: Follow-up with Bug 310310 - // In Linux SWT.DRAG is the only value received - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=310310 - if (e.detail == SWT.DRAG) { - _timeProvider.setStartFinishTime(time0, time1); - } else { - _timeProvider.setStartFinishTimeNotify(time0, time1); - } - } - } - - /** - * @return The current visibility of the vertical scroll bar - */ - public boolean isVisibleVerticalScroll() { - return _visibleVerticalScroll; - } - - @Override - public int getBorderWidth() { - return _borderWidth; - } - - /** - * Set the border width - * - * @param borderWidth - * The width - */ - public void setBorderWidth(int borderWidth) { - this._borderWidth = borderWidth; - } - - /** - * @return The current height of the header row - */ - public int getHeaderHeight() { - return _headerHeight; - } - - /** - * Set the height of the header row - * - * @param headerHeight - * The height - */ - public void setHeaderHeight(int headerHeight) { - this._headerHeight = headerHeight; - } - - /** - * @return The height of regular item rows - */ - public int getItemHeight() { - return _itemHeight; - } - - /** - * Set the height of regular itew rows - * - * @param rowHeight - * The height - */ - public void setItemHeight(int rowHeight) { - this._itemHeight = rowHeight; - } - - /** - * Set the minimum item width - * - * @param width The minimum width - */ - public void setMinimumItemWidth(int width) { - this._minimumItemWidth = width; - } - - /** - * @return The minimum item width - */ - public int getMinimumItemWidth() { - return _minimumItemWidth; - } - - /** - * @return The entries that are currently filtered out - */ - public Vector getFilteredOut() { - return _data.getFilteredOut(); - } - - // @Override - @Override - public void addSelectionChangedListener(ISelectionChangedListener listener) { - if (listener != null) { - if (!_selectionChangedListeners.contains(listener)) { - _selectionChangedListeners.add(listener); - } - } - } - - // @Override - @Override - public void removeSelectionChangedListener(ISelectionChangedListener listener) { - if (listener != null) { - _selectionChangedListeners.remove(listener); - } - } - - // @Override - @Override - public void setSelection(ISelection selection) { - if (selection instanceof TimeGraphSelection) { - TimeGraphSelection sel = (TimeGraphSelection) selection; - Object ob = sel.getFirstElement(); - if (ob instanceof ITimeGraphEntry) { - ITimeGraphEntry trace = (ITimeGraphEntry) ob; - selectItem(trace, false); - } - } - - } - - private class ItemData { - public Item[] _expandedItems = new Item[0]; - public Item[] _items = new Item[0]; - private ITimeGraphEntry _traces[] = new ITimeGraphEntry[0]; - private boolean traceFilter[] = new boolean[0]; - private final Vector filteredOut = new Vector(); - public ITimeGraphPresentationProvider provider; - - public ItemData() { - } - - Item findItem(ITimeGraphEntry entry) { - if (entry == null) { - return null; - } - - for (int i = 0; i < _items.length; i++) { - Item item = _items[i]; - if (item._trace == entry) { - return item; - } - } - - return null; - } - - int findItemIndex(ITimeGraphEntry trace) { - if (trace == null) { - return -1; - } - - for (int i = 0; i < _expandedItems.length; i++) { - Item item = _expandedItems[i]; - if (item._trace == trace) { - return i; - } - } - - return -1; - } - - public void refreshData() { - List itemList = new ArrayList(); - filteredOut.clear(); - for (int i = 0; i < _traces.length; i++) { - ITimeGraphEntry entry = _traces[i]; - refreshData(itemList, null, 0, entry); - } - _items = itemList.toArray(new Item[0]); - updateExpandedItems(); - } - - private void refreshData(List itemList, Item parent, int level, ITimeGraphEntry entry) { - Item item = new Item(entry, entry.getName(), level); - if (parent != null) { - parent.children.add(item); - } - item.itemHeight = provider.getItemHeight(entry); - itemList.add(item); - if (entry.hasChildren()) { - item._expanded = true; - item._hasChildren = true; - for (ITimeGraphEntry child : entry.getChildren()) { - refreshData(itemList, item, level + 1, child); - } - } - } - - public void updateExpandedItems() { - List expandedItemList = new ArrayList(); - for (int i = 0; i < _traces.length; i++) { - ITimeGraphEntry entry = _traces[i]; - Item item = findItem(entry); - refreshExpanded(expandedItemList, item); - } - _expandedItems = expandedItemList.toArray(new Item[0]); - } - - private void refreshExpanded(List expandedItemList, Item item) { - expandedItemList.add(item); - if (item._hasChildren && item._expanded) { - for (Item child : item.children) { - refreshExpanded(expandedItemList, child); - } - } - } - - public void refreshData(ITimeGraphEntry traces[]) { - if (traces == null || traces.length == 0) { - traceFilter = null; - } else if (traceFilter == null || traces.length != traceFilter.length) { - traceFilter = new boolean[traces.length]; - java.util.Arrays.fill(traceFilter, true); - } - - _traces = traces; - refreshData(); - } - - public ITimeGraphEntry[] getTraces() { - return _traces; - } - - public boolean[] getTraceFilter() { - return traceFilter; - } - - public Vector getFilteredOut() { - return filteredOut; - } - } - - private class Item { - public boolean _expanded; - public boolean _selected; - public boolean _hasChildren; - public int itemHeight; - public int level; - public List children; - public String _name; - public ITimeGraphEntry _trace; - - public Item(ITimeGraphEntry trace, String name, int level) { - this._trace = trace; - this._name = name; - this.level = level; - this.children = new ArrayList(); - } - - @Override - public String toString() { - return _name; - } - } - -} - +/***************************************************************************** + * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 Ericsson. + * 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 + * + * Contributors: + * Intel Corporation - Initial API and implementation + * Ruslan A. Scherbakov, Intel - Initial API and implementation + * Alvaro Sanchez-Leon - Updated for TMF + * Patrick Tasse - Refactoring + * + *****************************************************************************/ + +package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphTreeListener; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.StateItem; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent; +import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.MouseWheelListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +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.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.ScrollBar; + +/** + * Time graph control implementation + * + * @version 1.0 + * @author Alvaro Sanchez-Leon + * @author Patrick Tasse + */ +public class TimeGraphControl extends TimeGraphBaseControl implements FocusListener, KeyListener, MouseMoveListener, MouseListener, MouseWheelListener, ControlListener, SelectionListener, MouseTrackListener, TraverseListener, ISelectionProvider { + + private static final int DRAG_NONE = 0; + private static final int DRAG_TRACE_ITEM = 1; + private static final int DRAG_SPLIT_LINE = 2; + public static final boolean DEFAULT_DRAW_THREAD_JOIN = true; + public static final boolean DEFAULT_DRAW_THREAD_WAIT = true; + public static final boolean DEFAULT_DRAW_THREAD_RELEASE = true; + public static final int H_SCROLLBAR_MAX = Integer.MAX_VALUE - 1; + private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider + + private static final double zoomCoeff = 1.5; + + private ITimeDataProvider _timeProvider; + private boolean _isInFocus = false; + private boolean _isDragCursor3 = false; + private boolean _isWaitCursor = true; + private boolean _mouseOverSplitLine = false; + private int _itemHeight = CUSTOM_ITEM_HEIGHT; + private int _minimumItemWidth = 0; + private int _topIndex = 0; + private int _dragState = DRAG_NONE; + private int _dragX0 = 0; + private int _dragX = 0; + private int _idealNameSpace = 0; + // private double _timeStep = 10000000; + private long _time0bak; + private long _time1bak; + private ITimeGraphPresentationProvider fTimeGraphProvider = null; + private ItemData _data = null; + private List _selectionListeners; + private final List _selectionChangedListeners = new ArrayList(); + private final List _treeListeners = new ArrayList(); + private final Cursor _dragCursor3; + private final Cursor _WaitCursor; + + // Vertical formatting formatting for the state control view + private final boolean _visibleVerticalScroll = true; + private int _borderWidth = 0; + private int _headerHeight = 0; + + private Listener mouseScrollFilterListener; + + protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources()); + protected Color[] fEventColorMap = null; + + private MouseScrollNotifier fMouseScrollNotifier; + private final Object fMouseScrollNotifierLock = new Object(); + private class MouseScrollNotifier extends Thread { + private final static long DELAY = 400L; + private final static long POLLING_INTERVAL = 10L; + private long fLastScrollTime = Long.MAX_VALUE; + + @Override + public void run() { + while ((System.currentTimeMillis() - fLastScrollTime) < DELAY) { + try { + Thread.sleep(POLLING_INTERVAL); + } catch (Exception e) { + return; + } + } + if (!isInterrupted()) { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (isDisposed()) { + return; + } + _timeProvider.notifyStartFinishTime(); + } + }); + } + synchronized (fMouseScrollNotifierLock) { + fMouseScrollNotifier = null; + } + } + + public void mouseScrolled() { + fLastScrollTime = System.currentTimeMillis(); + } + } + + /** + * Standard constructor + * + * @param parent + * The parent composite object + * @param colors + * The color scheme to use + */ + public TimeGraphControl(Composite parent, TimeGraphColorScheme colors) { + + super(parent, colors, SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.DOUBLE_BUFFERED); + + _data = new ItemData(); + + addFocusListener(this); + addMouseListener(this); + addMouseMoveListener(this); + addMouseTrackListener(this); + addMouseWheelListener(this); + addTraverseListener(this); + addKeyListener(this); + addControlListener(this); + ScrollBar scrollHor = getHorizontalBar(); + + if (scrollHor != null) { + scrollHor.addSelectionListener(this); + } + + _dragCursor3 = new Cursor(super.getDisplay(), SWT.CURSOR_SIZEWE); + _WaitCursor = new Cursor(super.getDisplay(), SWT.CURSOR_WAIT); + } + + @Override + public void dispose() { + super.dispose(); + _dragCursor3.dispose(); + _WaitCursor.dispose(); + fResourceManager.dispose(); + } + + /** + * Sets the timegraph provider used by this timegraph viewer. + * + * @param timeGraphProvider the timegraph provider + */ + public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { + fTimeGraphProvider = timeGraphProvider; + _data.provider = timeGraphProvider; + + if (fEventColorMap != null) { + for (Color color : fEventColorMap) { + fResourceManager.destroyColor(color.getRGB()); + } + } + StateItem[] stateItems = fTimeGraphProvider.getStateTable(); + if (stateItems != null) { + fEventColorMap = new Color[stateItems.length]; + for (int i = 0; i < stateItems.length; i++) { + fEventColorMap[i] = fResourceManager.createColor(stateItems[i].getStateColor()); + } + } else { + fEventColorMap = new Color[] { }; + } + } + + /** + * Assign the given time provider + * + * @param timeProvider + * The time provider + */ + public void setTimeProvider(ITimeDataProvider timeProvider) { + _timeProvider = timeProvider; + adjustScrolls(); + redraw(); + } + + /** + * Add a selection listener + * + * @param listener + * The listener to add + */ + public void addSelectionListener(SelectionListener listener) { + if (listener == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + if (null == _selectionListeners) { + _selectionListeners = new ArrayList(); + } + _selectionListeners.add(listener); + } + + /** + * Remove a selection listener + * + * @param listener + * The listener to remove + */ + public void removeSelectionListener(SelectionListener listener) { + if (null != _selectionListeners) { + _selectionListeners.remove(listener); + } + } + + /** + * Selection changed callback + */ + public void fireSelectionChanged() { + if (null != _selectionListeners) { + Iterator it = _selectionListeners.iterator(); + while (it.hasNext()) { + SelectionListener listener = it.next(); + listener.widgetSelected(null); + } + } + } + + /** + * Default selection callback + */ + public void fireDefaultSelection() { + if (null != _selectionListeners) { + Iterator it = _selectionListeners.iterator(); + while (it.hasNext()) { + SelectionListener listener = it.next(); + listener.widgetDefaultSelected(null); + } + } + } + + /** + * Get the traces in the model + * + * @return The array of traces + */ + public ITimeGraphEntry[] getTraces() { + return _data.getTraces(); + } + + /** + * Get the on/off trace filters + * + * @return The array of filters + */ + public boolean[] getTraceFilter() { + return _data.getTraceFilter(); + } + + /** + * Refresh the data for the thing + */ + public void refreshData() { + _data.refreshData(); + adjustScrolls(); + redraw(); + } + + /** + * Refresh data for the given traces + * + * @param traces + * The traces to refresh + */ + public void refreshData(ITimeGraphEntry[] traces) { + _data.refreshData(traces); + adjustScrolls(); + redraw(); + } + + /** + * Adjust the scoll bars + */ + public void adjustScrolls() { + if (null == _timeProvider) { + getHorizontalBar().setValues(0, 1, 1, 1, 1, 1); + return; + } + + // HORIZONTAL BAR + // Visible window + long time0 = _timeProvider.getTime0(); + long time1 = _timeProvider.getTime1(); + // Time boundaries + long timeMin = _timeProvider.getMinTime(); + long timeMax = _timeProvider.getMaxTime(); + + long delta = timeMax - timeMin; + + int timePos = 0; + int thumb = H_SCROLLBAR_MAX; + + if (delta != 0) { + // Thumb size (page size) + thumb = Math.max(1, (int) (H_SCROLLBAR_MAX * ((double) (time1 - time0) / delta))); + // At the beginning of visible window + timePos = (int) (H_SCROLLBAR_MAX * ((double) (time0 - timeMin) / delta)); + } + + // position, minimum, maximum, thumb size, increment (half page)t, page + // increment size (full page) + getHorizontalBar().setValues(timePos, 0, H_SCROLLBAR_MAX, thumb, Math.max(1, thumb / 2), Math.max(2, thumb)); + } + + boolean ensureVisibleItem(int idx, boolean redraw) { + boolean changed = false; + if (idx < 0) { + for (idx = 0; idx < _data._expandedItems.length; idx++) { + if (_data._expandedItems[idx]._selected) { + break; + } + } + } + if (idx >= _data._expandedItems.length) { + return changed; + } + if (idx < _topIndex) { + setTopIndex(idx); + //FIXME:getVerticalBar().setSelection(_topItem); + if (redraw) { + redraw(); + } + changed = true; + } else { + int page = countPerPage(); + if (idx >= _topIndex + page) { + setTopIndex(idx - page + 1); + //FIXME:getVerticalBar().setSelection(_topItem); + if (redraw) { + redraw(); + } + changed = true; + } + } + return changed; + } + + /** + * Assign the given index as the top one + * + * @param idx + * The index + */ + public void setTopIndex(int idx) { + idx = Math.min(idx, _data._expandedItems.length - countPerPage()); + idx = Math.max(0, idx); + _topIndex = idx; + redraw(); + } + + /** + * Set the expanded state of a given entry + * + * @param entry + * The entry + * @param expanded + * True if expanded, false if collapsed + */ + public void setExpandedState(ITimeGraphEntry entry, boolean expanded) { + Item item = _data.findItem(entry); + if (item != null && item._expanded != expanded) { + item._expanded = expanded; + _data.updateExpandedItems(); + redraw(); + } + } + + /** + * Collapses all nodes of the viewer's tree, starting with the root. + * + * @since 2.0 + */ + public void collapseAll() { + for (Item item : _data._items) { + item._expanded = false; + } + _data.updateExpandedItems(); + redraw(); + } + + /** + * Expands all nodes of the viewer's tree, starting with the root. + * + * @since 2.0 + */ + public void expandAll() { + for (Item item : _data._items) { + item._expanded = true; + } + _data.updateExpandedItems(); + redraw(); + } + + /** + * Add a tree listener + * + * @param listener + * The listener to add + */ + public void addTreeListener(ITimeGraphTreeListener listener) { + if (!_treeListeners.contains(listener)) { + _treeListeners.add(listener); + } + } + + /** + * Remove a tree listener + * + * @param listener + * The listener to remove + */ + public void removeTreeListener(ITimeGraphTreeListener listener) { + if (_treeListeners.contains(listener)) { + _treeListeners.remove(listener); + } + } + + /** + * Tree event callback + * + * @param entry + * The affected entry + * @param expanded + * The expanded state (true for expanded, false for collapsed) + */ + public void fireTreeEvent(ITimeGraphEntry entry, boolean expanded) { + TimeGraphTreeExpansionEvent event = new TimeGraphTreeExpansionEvent(this, entry); + for (ITimeGraphTreeListener listener : _treeListeners) { + if (expanded) { + listener.treeExpanded(event); + } else { + listener.treeCollapsed(event); + } + } + } + + @Override + public ISelection getSelection() { + TimeGraphSelection sel = new TimeGraphSelection(); + ITimeGraphEntry trace = getSelectedTrace(); + if (null != trace && null != _timeProvider) { + long selectedTime = _timeProvider.getSelectedTime(); + ITimeEvent event = Utils.findEvent(trace, selectedTime, 0); + if (event != null) { + sel.add(event); + } else { + sel.add(trace); + } + } + return sel; + } + + /** + * Get the selection object + * + * @return The selection + */ + public ISelection getSelectionTrace() { + TimeGraphSelection sel = new TimeGraphSelection(); + ITimeGraphEntry trace = getSelectedTrace(); + if (null != trace) { + sel.add(trace); + } + return sel; + } + + /** + * Enable/disable one of the traces in the model + * + * @param n + * 1 to enable it, -1 to disable. The method returns immediately + * if another value is used. + */ + public void selectTrace(int n) { + if ((n != 1) && (n != -1)) { + return; + } + + boolean changed = false; + int lastSelection = -1; + for (int i = 0; i < _data._expandedItems.length; i++) { + Item item = _data._expandedItems[i]; + if (item._selected) { + lastSelection = i; + if ((1 == n) && (i < _data._expandedItems.length - 1)) { + item._selected = false; + item = _data._expandedItems[i + 1]; + item._selected = true; + changed = true; + } else if ((-1 == n) && (i > 0)) { + item._selected = false; + item = _data._expandedItems[i - 1]; + item._selected = true; + changed = true; + } + break; + } + } + + if (lastSelection < 0 && _data._expandedItems.length > 0) { + Item item = _data._expandedItems[0]; + item._selected = true; + changed = true; + } + + if (changed) { + ensureVisibleItem(-1, false); + redraw(); + fireSelectionChanged(); + } + } + + /** + * Select an event + * + * @param n + * 1 for next event, -1 for previous event + */ + public void selectEvent(int n) { + if (null == _timeProvider) { + return; + } + ITimeGraphEntry trace = getSelectedTrace(); + if (trace == null) { + return; + } + long selectedTime = _timeProvider.getSelectedTime(); + long endTime = _timeProvider.getEndTime(); + ITimeEvent nextEvent; + if (-1 == n && selectedTime > endTime) { + nextEvent = Utils.findEvent(trace, selectedTime, 0); + } else { + nextEvent = Utils.findEvent(trace, selectedTime, n); + } + if (null == nextEvent && -1 == n) { + nextEvent = Utils.getFirstEvent(trace); + } + if (null != nextEvent) { + long nextTime = nextEvent.getTime(); + // If last event detected e.g. going back or not moving to a next + // event + if (nextTime <= selectedTime && n == 1) { + // Select to the end of this last event + nextTime = nextEvent.getTime() + nextEvent.getDuration(); + // but not beyond the end of the trace + if (nextTime > endTime) { + nextTime = endTime; + } + } else if (n == -1) { + // for previous event go to its end time unless we were already there + if (nextEvent.getTime() + nextEvent.getDuration() < selectedTime) { + nextTime = nextEvent.getTime() + nextEvent.getDuration(); + } + } + _timeProvider.setSelectedTimeNotify(nextTime, true); + fireSelectionChanged(); + } else if (1 == n) { + _timeProvider.setSelectedTimeNotify(endTime, true); + fireSelectionChanged(); + } + } + + /** + * Select the next event + */ + public void selectNextEvent() { + selectEvent(1); + // Notify if visible time window has been adjusted + _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1()); + } + + /** + * Select the previous event + */ + public void selectPrevEvent() { + selectEvent(-1); + // Notify if visible time window has been adjusted + _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1()); + } + + /** + * Select the next trace + */ + public void selectNextTrace() { + selectTrace(1); + } + + /** + * Select the previous trace + */ + public void selectPrevTrace() { + selectTrace(-1); + } + + /** + * Zoom based on mouse cursor location with mouse scrolling + * + * @param zoomIn true to zoom in, false to zoom out + */ + public void zoom(boolean zoomIn) { + int globalX = getDisplay().getCursorLocation().x; + Point p = toControl(globalX, 0); + int nameSpace = _timeProvider.getNameSpace(); + int timeSpace = _timeProvider.getTimeSpace(); + int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x)); + long time0 = _timeProvider.getTime0(); + long time1 = _timeProvider.getTime1(); + long interval = time1 - time0; + if (interval == 0) { + interval = 1; + } // to allow getting out of single point interval + long newInterval; + if (zoomIn) { + newInterval = Math.max(Math.round(interval * 0.8), _timeProvider.getMinTimeInterval()); + } else { + newInterval = (long) Math.ceil(interval * 1.25); + } + long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval)); + long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval); + long newTime1 = newTime0 + newInterval; + _timeProvider.setStartFinishTime(newTime0, newTime1); + synchronized (fMouseScrollNotifierLock) { + if (fMouseScrollNotifier == null) { + fMouseScrollNotifier = new MouseScrollNotifier(); + fMouseScrollNotifier.start(); + } + fMouseScrollNotifier.mouseScrolled(); + } + } + + /** + * zoom in using single click + */ + public void zoomIn() { + long _time0 = _timeProvider.getTime0(); + long _time1 = _timeProvider.getTime1(); + long _range = _time1 - _time0; + long selTime = _timeProvider.getSelectedTime(); + if (selTime <= _time0 || selTime >= _time1) { + selTime = (_time0 + _time1) / 2; + } + long time0 = selTime - (long) ((selTime - _time0) / zoomCoeff); + long time1 = selTime + (long) ((_time1 - selTime) / zoomCoeff); + + long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0); + + // Trace.debug("selTime:" + selTime + " time0:" + time0 + " time1:" + // + time1 + " inaccuracy:" + inaccuracy); + + if (inaccuracy > 0 && inaccuracy < 100) { + _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime()); + return; + } + + long m = _timeProvider.getMinTimeInterval(); + if ((time1 - time0) < m) { + time0 = selTime - (selTime - _time0) * m / _range; + time1 = time0 + m; + } + + _timeProvider.setStartFinishTimeNotify(time0, time1); + } + + /** + * zoom out using single click + */ + public void zoomOut() { + long _time0 = _timeProvider.getTime0(); + long _time1 = _timeProvider.getTime1(); + long selTime = _timeProvider.getSelectedTime(); + if (selTime <= _time0 || selTime >= _time1) { + selTime = (_time0 + _time1) / 2; + } + long time0 = (long) (selTime - (selTime - _time0) * zoomCoeff); + long time1 = (long) (selTime + (_time1 - selTime) * zoomCoeff); + + long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0); + if (inaccuracy > 0 && inaccuracy < 100) { + _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime()); + return; + } + + _timeProvider.setStartFinishTimeNotify(time0, time1); + } + + /** + * Return the currently selected trace + * + * @return The entry matching the trace + */ + public ITimeGraphEntry getSelectedTrace() { + ITimeGraphEntry trace = null; + int idx = getSelectedIndex(); + if (idx >= 0) { + trace = _data._expandedItems[idx]._trace; + } + return trace; + } + + /** + * Retrieve the index of the currently selected item + * + * @return The index + */ + public int getSelectedIndex() { + int idx = -1; + for (int i = 0; i < _data._expandedItems.length; i++) { + Item item = _data._expandedItems[i]; + if (item._selected) { + idx = i; + break; + } + } + return idx; + } + + boolean toggle(int idx) { + boolean toggled = false; + if (idx >= 0 && idx < _data._expandedItems.length) { + Item item = _data._expandedItems[idx]; + if (item._hasChildren) { + item._expanded = !item._expanded; + _data.updateExpandedItems(); + adjustScrolls(); + redraw(); + toggled = true; + fireTreeEvent(item._trace, item._expanded); + } + } + return toggled; + } + + int getItemIndexAtY(int y) { + if (y < 0) { + return -1; + } + if (_itemHeight == CUSTOM_ITEM_HEIGHT) { + int ySum = 0; + for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) { + ySum += _data._expandedItems[idx].itemHeight; + if (y < ySum) { + return idx; + } + } + return -1; + } + int idx = y / _itemHeight; + idx += _topIndex; + if (idx < _data._expandedItems.length) { + return idx; + } + return -1; + } + + boolean isOverSplitLine(int x) { + if (x < 0 || null == _timeProvider) { + return false; + } + int w = 4; + int nameWidth = _timeProvider.getNameSpace(); + if (x > nameWidth - w && x < nameWidth + w) { + return true; + } + return false; + } + + ITimeGraphEntry getEntry(Point pt) { + int idx = getItemIndexAtY(pt.y); + return idx >= 0 ? _data._expandedItems[idx]._trace : null; + } + + long getTimeAtX(int x) { + if (null == _timeProvider) { + return -1; + } + long hitTime = -1; + Point size = getCtrlSize(); + long time0 = _timeProvider.getTime0(); + long time1 = _timeProvider.getTime1(); + int nameWidth = _timeProvider.getNameSpace(); + x -= nameWidth; + int timeWidth = size.x - nameWidth - RIGHT_MARGIN; + if (x >= 0 && size.x >= nameWidth) { + if (time1 - time0 > timeWidth) { + // nanosecond smaller than one pixel: use the first integer nanosecond of this pixel's time range + hitTime = time0 + (long) Math.ceil((time1 - time0) * ((double) x / timeWidth)); + } else { + // nanosecond greater than one pixel: use the nanosecond that covers this pixel start position + hitTime = time0 + (long) Math.floor((time1 - time0) * ((double) x / timeWidth)); + } + } + return hitTime; + } + + void selectItem(int idx, boolean addSelection) { + boolean changed = false; + if (addSelection) { + if (idx >= 0 && idx < _data._expandedItems.length) { + Item item = _data._expandedItems[idx]; + changed = (item._selected == false); + item._selected = true; + } + } else { + for (int i = 0; i < _data._expandedItems.length; i++) { + Item item = _data._expandedItems[i]; + if ((i == idx && !item._selected) || (idx == -1 && item._selected)) { + changed = true; + } + item._selected = i == idx; + } + } + changed |= ensureVisibleItem(idx, true); + if (changed) { + redraw(); + } + } + + /** + * Callback for item selection + * + * @param trace + * The entry matching the trace + * @param addSelection + * If the selection is added or removed + */ + public void selectItem(ITimeGraphEntry trace, boolean addSelection) { + int idx = _data.findItemIndex(trace); + selectItem(idx, addSelection); + } + + /** + * Retrieve the number of entries shown per page. + * + * @return The count + */ + public int countPerPage() { + int height = getCtrlSize().y; + int count = 0; + if (_itemHeight == CUSTOM_ITEM_HEIGHT) { + int ySum = 0; + for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) { + ySum += _data._expandedItems[idx].itemHeight; + if (ySum >= height) { + return count; + } + count++; + } + for (int idx = _topIndex - 1; idx >= 0; idx--) { + ySum += _data._expandedItems[idx].itemHeight; + if (ySum >= height) { + return count; + } + count++; + } + return count; + } + if (height > 0) { + count = height / _itemHeight; + } + return count; + } + + /** + * Get the index of the top element + * + * @return The index + */ + public int getTopIndex() { + return _topIndex; + } + + /** + * Get the number of expanded items + * + * @return The count of expanded items + */ + public int getExpandedElementCount() { + return _data._expandedItems.length; + } + + /** + * Get an array of all expanded elements + * + * @return The expanded elements + */ + public ITimeGraphEntry[] getExpandedElements() { + ArrayList elements = new ArrayList(); + for (Item item : _data._expandedItems) { + elements.add(item._trace); + } + return elements.toArray(new ITimeGraphEntry[0]); + } + + Point getCtrlSize() { + Point size = getSize(); + if (getHorizontalBar().isVisible()) { + size.y -= getHorizontalBar().getSize().y; + } + return size; + } + + Rectangle getNameRect(Rectangle bound, int idx, int nameWidth) { + int x = bound.x; + int y = bound.y + (idx - _topIndex) * _itemHeight; + int width = nameWidth; + int height = _itemHeight; + if (_itemHeight == CUSTOM_ITEM_HEIGHT) { + int ySum = 0; + for (int i = _topIndex; i < idx; i++) { + ySum += _data._expandedItems[i].itemHeight; + } + y = bound.y + ySum; + height = _data._expandedItems[idx].itemHeight; + } + return new Rectangle(x, y, width, height); + } + + Rectangle getStatesRect(Rectangle bound, int idx, int nameWidth) { + int x = bound.x + nameWidth; + int y = bound.y + (idx - _topIndex) * _itemHeight; + int width = bound.width - x; + int height = _itemHeight; + if (_itemHeight == CUSTOM_ITEM_HEIGHT) { + int ySum = 0; + for (int i = _topIndex; i < idx; i++) { + ySum += _data._expandedItems[i].itemHeight; + } + y = bound.y + ySum; + height = _data._expandedItems[idx].itemHeight; + } + return new Rectangle(x, y, width, height); + } + + @Override + void paint(Rectangle bounds, PaintEvent e) { + GC gc = e.gc; + gc.setBackground(_colors.getColor(TimeGraphColorScheme.BACKGROUND)); + drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height); + + if (bounds.width < 2 || bounds.height < 2 || null == _timeProvider) { + return; + } + + _idealNameSpace = 0; + int nameSpace = _timeProvider.getNameSpace(); + + // draw empty name space background + gc.setBackground(_colors.getBkColor(false, false, true)); + drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height); + + drawItems(bounds, _timeProvider, _data._expandedItems, _topIndex, nameSpace, gc); + + // draw selected time + long time0 = _timeProvider.getTime0(); + long time1 = _timeProvider.getTime1(); + long selectedTime = _timeProvider.getSelectedTime(); + double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0); + int x = bounds.x + nameSpace + (int) ((selectedTime - time0) * pixelsPerNanoSec); + if (x >= nameSpace && x < bounds.x + bounds.width) { + gc.setForeground(_colors.getColor(TimeGraphColorScheme.SELECTED_TIME)); + gc.drawLine(x, bounds.y, x, bounds.y + bounds.height); + } + + // draw drag line, no line if name space is 0. + if (DRAG_SPLIT_LINE == _dragState) { + gc.setForeground(_colors.getColor(TimeGraphColorScheme.BLACK)); + gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1); + } else if (DRAG_NONE == _dragState && _mouseOverSplitLine && _timeProvider.getNameSpace() > 0) { + gc.setForeground(_colors.getColor(TimeGraphColorScheme.RED)); + gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1); + } + } + + /** + * Draw many items at once + * + * @param bounds + * The rectangle of the area + * @param timeProvider + * The time provider + * @param items + * The array items to draw + * @param topIndex + * The index of the first element to draw + * @param nameSpace + * The width reserved for the names + * @param gc + * Reference to the SWT GC object + */ + public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider, + Item[] items, int topIndex, int nameSpace, GC gc) { + for (int i = topIndex; i < items.length; i++) { + Item item = items[i]; + drawItem(item, bounds, timeProvider, i, nameSpace, gc); + } + fTimeGraphProvider.postDrawControl(bounds, gc); + } + + /** + * Draws the item + * + * @param item the item to draw + * @param bounds the container rectangle + * @param i the item index + * @param nameSpace the name space + * @param gc + */ + protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) { + ITimeGraphEntry entry = item._trace; + long time0 = timeProvider.getTime0(); + long time1 = timeProvider.getTime1(); + long selectedTime = timeProvider.getSelectedTime(); + + Rectangle nameRect = getNameRect(bounds, i, nameSpace); + if (nameRect.y >= bounds.y + bounds.height) { + return; + } + + if (! item._trace.hasTimeEvents()) { + Rectangle statesRect = getStatesRect(bounds, i, nameSpace); + nameRect.width += statesRect.width; + drawName(item, nameRect, gc); + } else { + drawName(item, nameRect, gc); + } + Rectangle rect = getStatesRect(bounds, i, nameSpace); + if (rect.isEmpty()) { + fTimeGraphProvider.postDrawEntry(entry, rect, gc); + return; + } + if (time1 <= time0) { + gc.setBackground(_colors.getBkColor(false, false, false)); + gc.fillRectangle(rect); + fTimeGraphProvider.postDrawEntry(entry, rect, gc); + return; + } + + // Initialize _rect1 to same values as enclosing rectangle rect + Rectangle stateRect = Utils.clone(rect); + boolean selected = item._selected; + // K pixels per second + double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0); + + if (item._trace.hasTimeEvents()) { + fillSpace(rect, gc, selected); + // Drawing rectangle is smaller than reserved space + stateRect.y += 3; + stateRect.height -= 6; + + long maxDuration = (timeProvider.getTimeSpace() == 0) ? Long.MAX_VALUE : 1 * (time1 - time0) / timeProvider.getTimeSpace(); + Iterator iterator = entry.getTimeEventsIterator(time0, time1, maxDuration); + + int lastX = -1; + while (iterator.hasNext()) { + ITimeEvent event = iterator.next(); + int x = rect.x + (int) ((event.getTime() - time0) * pixelsPerNanoSec); + int xEnd = rect.x + (int) ((event.getTime() + event.getDuration() - time0) * pixelsPerNanoSec); + if (x >= rect.x + rect.width || xEnd < rect.x) { + // event is out of bounds + continue; + } + xEnd = Math.min(rect.x + rect.width, xEnd); + stateRect.x = Math.max(rect.x, x); + stateRect.width = Math.max(0, xEnd - stateRect.x + 1); + if (stateRect.x == lastX) { + stateRect.width -= 1; + if (stateRect.width > 0) { + gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); + gc.drawPoint(stateRect.x, stateRect.y - 2); + stateRect.x += 1; + } + } else { + lastX = x; + } + boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration(); + drawState(_colors, event, stateRect, gc, selected, timeSelected); + } + } + fTimeGraphProvider.postDrawEntry(entry, rect, gc); + } + + protected void drawName(Item item, Rectangle bounds, GC gc) { + boolean hasTimeEvents = item._trace.hasTimeEvents(); + if (! hasTimeEvents) { + gc.setBackground(_colors.getBkColorGroup(item._selected, _isInFocus)); + gc.fillRectangle(bounds); + if (item._selected && _isInFocus) { + gc.setForeground(_colors.getBkColor(item._selected, _isInFocus, false)); + gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1); + } + } else { + gc.setBackground(_colors.getBkColor(item._selected, _isInFocus, true)); + gc.setForeground(_colors.getFgColor(item._selected, _isInFocus)); + gc.fillRectangle(bounds); + } + + // No name to be drawn + if (_timeProvider.getNameSpace() == 0) { + return; + } + + int leftMargin = MARGIN + item.level * EXPAND_SIZE; + if (item._hasChildren) { + gc.setForeground(_colors.getFgColorGroup(false, false)); + gc.setBackground(_colors.getBkColor(false, false, false)); + Rectangle rect = Utils.clone(bounds); + rect.x += leftMargin; + rect.y += (bounds.height - EXPAND_SIZE) / 2; + rect.width = EXPAND_SIZE; + rect.height = EXPAND_SIZE; + gc.fillRectangle(rect); + gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1); + int midy = rect.y + rect.height / 2; + gc.drawLine(rect.x + 2, midy, rect.x + rect.width - 3, midy); + if (!item._expanded) { + int midx = rect.x + rect.width / 2; + gc.drawLine(midx, rect.y + 2, midx, rect.y + rect.height - 3); + } + } + leftMargin += EXPAND_SIZE + MARGIN; + + Image img = fTimeGraphProvider.getItemImage(item._trace); + if (img != null) { + // draw icon + int imgHeight = img.getImageData().height; + int imgWidth = img.getImageData().width; + int x = leftMargin; + int y = bounds.y + (bounds.height - imgHeight) / 2; + gc.drawImage(img, x, y); + leftMargin += imgWidth + MARGIN; + } + String name = item._name; + Point size = gc.stringExtent(name); + if (_idealNameSpace < leftMargin + size.x + MARGIN) { + _idealNameSpace = leftMargin + size.x + MARGIN; + } + if (hasTimeEvents) { + // cut long string with "..." + int width = bounds.width - leftMargin; + int cuts = 0; + while (size.x > width && name.length() > 1) { + cuts++; + name = name.substring(0, name.length() - 1); + size = gc.stringExtent(name + "..."); //$NON-NLS-1$ + } + if (cuts > 0) { + name += "..."; //$NON-NLS-1$ + } + } + Rectangle rect = Utils.clone(bounds); + rect.x += leftMargin; + rect.width -= leftMargin; + // draw text + if (rect.width > 0) { + rect.y += (bounds.height - gc.stringExtent(name).y) / 2; + gc.setForeground(_colors.getFgColor(item._selected, _isInFocus)); + int textWidth = Utils.drawText(gc, name, rect, true); + leftMargin += textWidth + MARGIN; + rect.y -= 2; + + if (hasTimeEvents) { + // draw middle line + int x = bounds.x + leftMargin; + int width = bounds.width - x; + int midy = bounds.y + bounds.height / 2; + gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE)); + gc.drawLine(x, midy, x + width, midy); + } + } + } + + protected void drawState(TimeGraphColorScheme colors, ITimeEvent event, + Rectangle rect, GC gc, boolean selected, boolean timeSelected) { + + int colorIdx = fTimeGraphProvider.getStateTableIndex(event); + if (colorIdx < 0) { + return; + } + boolean visible = rect.width == 0 ? false : true; + + if (visible) { + Color stateColor = null; + if (colorIdx < fEventColorMap.length) { + stateColor = fEventColorMap[colorIdx]; + } else { + stateColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK); + } + + timeSelected = timeSelected && selected; + if (timeSelected) { + // modify the color? + } + // fill all rect area + gc.setBackground(stateColor); + gc.fillRectangle(rect); + // get the border color? + gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); + + // draw bounds + if (!timeSelected) { + // Draw the top and bottom borders i.e. no side borders + // top + gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y); + // bottom + gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1); + } + } else { + gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); + gc.drawPoint(rect.x, rect.y - 2); + /* + // selected rectangle area is not visible but can be represented + // with a broken vertical line of specified width. + int width = 1; + rect.width = width; + gc.setForeground(stateColor); + int s = gc.getLineStyle(); + int w = gc.getLineWidth(); + gc.setLineStyle(SWT.LINE_DOT); + gc.setLineWidth(width); + // Trace.debug("Rectangle not visible, drawing vertical line with: " + // + rect.x + "," + rect.y + "," + rect.x + "," + rect.y + // + rect.height); + gc.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1); + gc.setLineStyle(s); + gc.setLineWidth(w); + if (!timeSelected) { + gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); + gc.drawPoint(rect.x, rect.y); + gc.drawPoint(rect.x, rect.y + rect.height - 1); + } + */ + } + fTimeGraphProvider.postDrawEvent(event, rect, gc); + } + + protected void fillSpace(Rectangle rect, GC gc, boolean selected) { + gc.setBackground(_colors.getBkColor(selected, _isInFocus, false)); + gc.fillRectangle(rect); + // draw middle line + gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE)); + int midy = rect.y + rect.height / 2; + gc.drawLine(rect.x, midy, rect.x + rect.width, midy); + } + + @Override + public void keyTraversed(TraverseEvent e) { + if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) { + e.doit = true; + } + } + + @Override + public void keyPressed(KeyEvent e) { + int idx = -1; + if (_data._expandedItems.length == 0) { + return; + } + if (SWT.HOME == e.keyCode) { + idx = 0; + } else if (SWT.END == e.keyCode) { + idx = _data._expandedItems.length - 1; + } else if (SWT.ARROW_DOWN == e.keyCode) { + idx = getSelectedIndex(); + if (idx < 0) { + idx = 0; + } else if (idx < _data._expandedItems.length - 1) { + idx++; + } + } else if (SWT.ARROW_UP == e.keyCode) { + idx = getSelectedIndex(); + if (idx < 0) { + idx = 0; + } else if (idx > 0) { + idx--; + } + } else if (SWT.ARROW_LEFT == e.keyCode) { + selectPrevEvent(); + } else if (SWT.ARROW_RIGHT == e.keyCode) { + selectNextEvent(); + } else if (SWT.PAGE_DOWN == e.keyCode) { + int page = countPerPage(); + idx = getSelectedIndex(); + if (idx < 0) { + idx = 0; + } + idx += page; + if (idx >= _data._expandedItems.length) { + idx = _data._expandedItems.length - 1; + } + } else if (SWT.PAGE_UP == e.keyCode) { + int page = countPerPage(); + idx = getSelectedIndex(); + if (idx < 0) { + idx = 0; + } + idx -= page; + if (idx < 0) { + idx = 0; + } + } else if (SWT.CR == e.keyCode) { + idx = getSelectedIndex(); + if (idx >= 0) { + if (_data._expandedItems[idx]._hasChildren) { + toggle(idx); + } else { + fireDefaultSelection(); + } + } + idx = -1; + } + if (idx >= 0) { + selectItem(idx, false); + fireSelectionChanged(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void focusGained(FocusEvent e) { + _isInFocus = true; + if (mouseScrollFilterListener == null) { + mouseScrollFilterListener = new Listener() { + // This filter is used to prevent horizontal scrolling of the view + // when the mouse wheel is used to zoom + @Override + public void handleEvent(Event event) { + event.doit = false; + } + }; + getDisplay().addFilter(SWT.MouseWheel, mouseScrollFilterListener); + } + redraw(); + } + + @Override + public void focusLost(FocusEvent e) { + _isInFocus = false; + if (mouseScrollFilterListener != null) { + getDisplay().removeFilter(SWT.MouseWheel, mouseScrollFilterListener); + mouseScrollFilterListener = null; + } + if (DRAG_NONE != _dragState) { + setCapture(false); + _dragState = DRAG_NONE; + } + redraw(); + } + + /** + * @return If the current view is focused + */ + public boolean isInFocus() { + return _isInFocus; + } + + /** + * Provide the possibility to control the wait cursor externally e.g. data + * requests in progress + * + * @param waitInd Should we wait indefinitely? + */ + public void waitCursor(boolean waitInd) { + // Update cursor as indicated + if (waitInd) { + setCursor(_WaitCursor); + _isWaitCursor = true; + } else { + setCursor(null); + _isWaitCursor = false; + } + + // Get ready for next mouse move + _isDragCursor3 = false; + } + + /** + *

+ * If the x, y position is over the vertical split line (name to time + * ranges), then change the cursor to a drag cursor to indicate the user the + * possibility of resizing + *

+ * + * @param x + * @param y + */ + void updateCursor(int x, int y) { + // if Wait cursor not active, check for the need to change to a drag + // cursor + if (_isWaitCursor == false) { + boolean isSplitLine = isOverSplitLine(x); + // No dragcursor is name space is fixed to zero + if (isSplitLine && !_isDragCursor3 && _timeProvider.getNameSpace() > 0) { + setCursor(_dragCursor3); + _isDragCursor3 = true; + } else if (!isSplitLine && _isDragCursor3) { + setCursor(null); + _isDragCursor3 = false; + } + } + } + + @Override + public void mouseMove(MouseEvent e) { + if (null == _timeProvider) { + return; + } + Point size = getCtrlSize(); + if (DRAG_TRACE_ITEM == _dragState) { + int nameWidth = _timeProvider.getNameSpace(); + int x = e.x - nameWidth; + if (x > 0 && size.x > nameWidth && _dragX != x) { + _dragX = x; + double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (_time1bak - _time0bak); + long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((_dragX - _dragX0) / pixelsPerNanoSec)); + long time1 = _time1bak - timeDelta; + long maxTime = _timeProvider.getMaxTime(); + if (time1 > maxTime) { + time1 = maxTime; + } + long time0 = time1 - (_time1bak - _time0bak); + if (time0 < _timeProvider.getMinTime()) { + time0 = _timeProvider.getMinTime(); + time1 = time0 + (_time1bak - _time0bak); + } + _timeProvider.setStartFinishTime(time0, time1); + } + } else if (DRAG_SPLIT_LINE == _dragState) { + _dragX = e.x; + _timeProvider.setNameSpace(e.x); + } else if (DRAG_NONE == _dragState) { + boolean mouseOverSplitLine = isOverSplitLine(e.x); + if (_mouseOverSplitLine != mouseOverSplitLine) { + redraw(); + } + _mouseOverSplitLine = mouseOverSplitLine; + } + updateCursor(e.x, e.y); + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + if (null == _timeProvider) { + return; + } + if (1 == e.button) { + if (isOverSplitLine(e.x) && _timeProvider.getNameSpace() != 0) { + _timeProvider.setNameSpace(_idealNameSpace); + boolean mouseOverSplitLine = isOverSplitLine(e.x); + if (_mouseOverSplitLine != mouseOverSplitLine) { + redraw(); + } + _mouseOverSplitLine = mouseOverSplitLine; + return; + } + int idx = getItemIndexAtY(e.y); + if (idx >= 0) { + selectItem(idx, false); + fireDefaultSelection(); + } + } + } + + @Override + public void mouseDown(MouseEvent e) { + if (null == _timeProvider) { + return; + } + int idx; + if (1 == e.button) { + int nameSpace = _timeProvider.getNameSpace(); + if (nameSpace != 0) { + if (isOverSplitLine(e.x)) { + _dragState = DRAG_SPLIT_LINE; + _dragX = _dragX0 = e.x; + _time0bak = _timeProvider.getTime0(); + _time1bak = _timeProvider.getTime1(); + redraw(); + return; + } + } + + idx = getItemIndexAtY(e.y); + if (idx >= 0) { + Item item = _data._expandedItems[idx]; + if (item._hasChildren && e.x < nameSpace && e.x < MARGIN + (item.level + 1) * EXPAND_SIZE) { + toggle(idx); + } else { + long hitTime = getTimeAtX(e.x); + if (hitTime >= 0) { + // _timeProvider.setSelectedTimeInt(hitTime, false); + setCapture(true); + _dragState = DRAG_TRACE_ITEM; + _dragX = _dragX0 = e.x - nameSpace; + _time0bak = _timeProvider.getTime0(); + _time1bak = _timeProvider.getTime1(); + } + } + selectItem(idx, false); + fireSelectionChanged(); + } else { + selectItem(idx, false); // clear selection + redraw(); + fireSelectionChanged(); + } + } + } + + @Override + public void mouseUp(MouseEvent e) { + if (DRAG_NONE != _dragState) { + setCapture(false); + if (DRAG_TRACE_ITEM == _dragState) { + // Notify time provider to check the need for listener + // notification + _timeProvider.notifyStartFinishTime(); + if (_dragX == _dragX0) { // click without drag + long time = getTimeAtX(e.x); + _timeProvider.setSelectedTimeNotify(time, false); + } + } else if (DRAG_SPLIT_LINE == _dragState) { + redraw(); + } + _dragState = DRAG_NONE; + } + } + + @Override + public void mouseEnter(MouseEvent e) { + } + + @Override + public void mouseExit(MouseEvent e) { + if (_mouseOverSplitLine) { + _mouseOverSplitLine = false; + redraw(); + } + } + + @Override + public void mouseHover(MouseEvent e) { + } + + @Override + public void mouseScrolled(MouseEvent e) { + if ((mouseScrollFilterListener == null) || _dragState != DRAG_NONE) { + return; + } + boolean zoomScroll = false; + Point p = getParent().toControl(getDisplay().getCursorLocation()); + Point parentSize = getParent().getSize(); + if (p.x >= 0 && p.x < parentSize.x && p.y >= 0 && p.y < parentSize.y) { + // over the parent control + if (e.x > getCtrlSize().x) { + // over the horizontal scroll bar + zoomScroll = false; + } else if (e.y >= 0 && e.y < getCtrlSize().y && e.x < _timeProvider.getNameSpace()) { + // over the name space + zoomScroll = false; + } else { + zoomScroll = true; + } + } + if (zoomScroll && _timeProvider.getTime0() != _timeProvider.getTime1()) { + if (e.count > 0) { + zoom(true); + } else if (e.count < 0) { + zoom(false); + } + } else { + setTopIndex(getTopIndex() - e.count); + } + } + + @Override + public void controlMoved(ControlEvent e) { + } + + @Override + public void controlResized(ControlEvent e) { + adjustScrolls(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.widget == getVerticalBar()) { + setTopIndex(getVerticalBar().getSelection()); + } else if (e.widget == getHorizontalBar() && null != _timeProvider) { + int start = getHorizontalBar().getSelection(); + long time0 = _timeProvider.getTime0(); + long time1 = _timeProvider.getTime1(); + long timeMin = _timeProvider.getMinTime(); + long timeMax = _timeProvider.getMaxTime(); + long delta = timeMax - timeMin; + + long range = time1 - time0; + // _timeRangeFixed = true; + time0 = timeMin + Math.round(delta * ((double) start / H_SCROLLBAR_MAX)); + time1 = time0 + range; + + // TODO: Follow-up with Bug 310310 + // In Linux SWT.DRAG is the only value received + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=310310 + if (e.detail == SWT.DRAG) { + _timeProvider.setStartFinishTime(time0, time1); + } else { + _timeProvider.setStartFinishTimeNotify(time0, time1); + } + } + } + + /** + * @return The current visibility of the vertical scroll bar + */ + public boolean isVisibleVerticalScroll() { + return _visibleVerticalScroll; + } + + @Override + public int getBorderWidth() { + return _borderWidth; + } + + /** + * Set the border width + * + * @param borderWidth + * The width + */ + public void setBorderWidth(int borderWidth) { + this._borderWidth = borderWidth; + } + + /** + * @return The current height of the header row + */ + public int getHeaderHeight() { + return _headerHeight; + } + + /** + * Set the height of the header row + * + * @param headerHeight + * The height + */ + public void setHeaderHeight(int headerHeight) { + this._headerHeight = headerHeight; + } + + /** + * @return The height of regular item rows + */ + public int getItemHeight() { + return _itemHeight; + } + + /** + * Set the height of regular itew rows + * + * @param rowHeight + * The height + */ + public void setItemHeight(int rowHeight) { + this._itemHeight = rowHeight; + } + + /** + * Set the minimum item width + * + * @param width The minimum width + */ + public void setMinimumItemWidth(int width) { + this._minimumItemWidth = width; + } + + /** + * @return The minimum item width + */ + public int getMinimumItemWidth() { + return _minimumItemWidth; + } + + /** + * @return The entries that are currently filtered out + */ + public Vector getFilteredOut() { + return _data.getFilteredOut(); + } + + // @Override + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (listener != null) { + if (!_selectionChangedListeners.contains(listener)) { + _selectionChangedListeners.add(listener); + } + } + } + + // @Override + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + if (listener != null) { + _selectionChangedListeners.remove(listener); + } + } + + // @Override + @Override + public void setSelection(ISelection selection) { + if (selection instanceof TimeGraphSelection) { + TimeGraphSelection sel = (TimeGraphSelection) selection; + Object ob = sel.getFirstElement(); + if (ob instanceof ITimeGraphEntry) { + ITimeGraphEntry trace = (ITimeGraphEntry) ob; + selectItem(trace, false); + } + } + + } + + private class ItemData { + public Item[] _expandedItems = new Item[0]; + public Item[] _items = new Item[0]; + private ITimeGraphEntry _traces[] = new ITimeGraphEntry[0]; + private boolean traceFilter[] = new boolean[0]; + private final Vector filteredOut = new Vector(); + public ITimeGraphPresentationProvider provider; + + public ItemData() { + } + + Item findItem(ITimeGraphEntry entry) { + if (entry == null) { + return null; + } + + for (int i = 0; i < _items.length; i++) { + Item item = _items[i]; + if (item._trace == entry) { + return item; + } + } + + return null; + } + + int findItemIndex(ITimeGraphEntry trace) { + if (trace == null) { + return -1; + } + + for (int i = 0; i < _expandedItems.length; i++) { + Item item = _expandedItems[i]; + if (item._trace == trace) { + return i; + } + } + + return -1; + } + + public void refreshData() { + List itemList = new ArrayList(); + filteredOut.clear(); + for (int i = 0; i < _traces.length; i++) { + ITimeGraphEntry entry = _traces[i]; + refreshData(itemList, null, 0, entry); + } + _items = itemList.toArray(new Item[0]); + updateExpandedItems(); + } + + private void refreshData(List itemList, Item parent, int level, ITimeGraphEntry entry) { + Item item = new Item(entry, entry.getName(), level); + if (parent != null) { + parent.children.add(item); + } + item.itemHeight = provider.getItemHeight(entry); + itemList.add(item); + if (entry.hasChildren()) { + item._expanded = true; + item._hasChildren = true; + for (ITimeGraphEntry child : entry.getChildren()) { + refreshData(itemList, item, level + 1, child); + } + } + } + + public void updateExpandedItems() { + List expandedItemList = new ArrayList(); + for (int i = 0; i < _traces.length; i++) { + ITimeGraphEntry entry = _traces[i]; + Item item = findItem(entry); + refreshExpanded(expandedItemList, item); + } + _expandedItems = expandedItemList.toArray(new Item[0]); + } + + private void refreshExpanded(List expandedItemList, Item item) { + expandedItemList.add(item); + if (item._hasChildren && item._expanded) { + for (Item child : item.children) { + refreshExpanded(expandedItemList, child); + } + } + } + + public void refreshData(ITimeGraphEntry traces[]) { + if (traces == null || traces.length == 0) { + traceFilter = null; + } else if (traceFilter == null || traces.length != traceFilter.length) { + traceFilter = new boolean[traces.length]; + java.util.Arrays.fill(traceFilter, true); + } + + _traces = traces; + refreshData(); + } + + public ITimeGraphEntry[] getTraces() { + return _traces; + } + + public boolean[] getTraceFilter() { + return traceFilter; + } + + public Vector getFilteredOut() { + return filteredOut; + } + } + + private class Item { + public boolean _expanded; + public boolean _selected; + public boolean _hasChildren; + public int itemHeight; + public int level; + public List children; + public String _name; + public ITimeGraphEntry _trace; + + public Item(ITimeGraphEntry trace, String name, int level) { + this._trace = trace; + this._name = name; + this.level = level; + this.children = new ArrayList(); + } + + @Override + public String toString() { + return _name; + } + } + +} + -- 2.34.1