| /***************************************************************************** |
| * Copyright (c) 2007, 2015 Intel Corporation and others |
| * |
| * 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, Ericsson - Updated for TMF |
| * Patrick Tasse, Ericsson - Refactoring |
| * Geneviève Bastien, École Polytechnique de Montréal - Move code to |
| * provide base classes for time graph view |
| * Add display of links between items |
| * Xavier Raynaud, Kalray - Code optimization |
| * Generoso Pagano, Inria - Support for drag selection listeners |
| *****************************************************************************/ |
| |
| package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.resource.LocalResourceManager; |
| import org.eclipse.jface.viewers.AbstractTreeViewer; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.ViewerFilter; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| 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.MenuDetectEvent; |
| import org.eclipse.swt.events.MenuDetectListener; |
| 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.SelectionListener; |
| import org.eclipse.swt.events.TraverseEvent; |
| import org.eclipse.swt.events.TraverseListener; |
| import org.eclipse.swt.events.TypedEvent; |
| 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.tracecompass.tmf.core.signal.TmfSignalManager; |
| import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo; |
| import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal; |
| import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphColorListener; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTreeListener; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat; |
| |
| /** |
| * Time graph control implementation |
| * |
| * @author Alvaro Sanchez-Leon |
| * @author Patrick Tasse |
| */ |
| public class TimeGraphControl extends TimeGraphBaseControl |
| implements FocusListener, KeyListener, MouseMoveListener, MouseListener, |
| MouseWheelListener, MouseTrackListener, TraverseListener, ISelectionProvider, |
| MenuDetectListener, ITmfTimeGraphDrawingHelper, ITimeGraphColorListener, Listener { |
| |
| /** Constant indicating that all levels of the time graph should be expanded */ |
| public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS; |
| |
| private static final int DRAG_NONE = 0; |
| private static final int DRAG_TRACE_ITEM = 1; |
| private static final int DRAG_SPLIT_LINE = 2; |
| private static final int DRAG_ZOOM = 3; |
| private static final int DRAG_SELECTION = 4; |
| |
| private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider |
| |
| private static final double ZOOM_FACTOR = 1.5; |
| private static final double ZOOM_IN_FACTOR = 0.8; |
| private static final double ZOOM_OUT_FACTOR = 1.25; |
| |
| private static final int SNAP_WIDTH = 2; |
| private static final int ARROW_HOVER_MAX_DIST = 5; |
| |
| private static final int NO_STATUS = -1; |
| private static final int STATUS_WITHOUT_CURSOR_TIME = -2; |
| |
| /** Resource manager */ |
| private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources()); |
| |
| /** Color map for event types */ |
| private Color[] fEventColorMap = null; |
| |
| private ITimeDataProvider fTimeProvider; |
| private IStatusLineManager fStatusLineManager = null; |
| private TimeGraphScale fTimeGraphScale = null; |
| |
| private boolean fIsInFocus = false; |
| private boolean fMouseOverSplitLine = false; |
| private int fGlobalItemHeight = CUSTOM_ITEM_HEIGHT; |
| private boolean fBlendSubPixelEvents = false; |
| private int fMinimumItemWidth = 0; |
| private int fTopIndex = 0; |
| private int fDragState = DRAG_NONE; |
| private boolean fDragBeginMarker = false; |
| private int fDragButton; |
| private int fDragX0 = 0; |
| private int fDragX = 0; |
| private long fDragTime0 = 0; // used to preserve accuracy of modified selection |
| private int fIdealNameSpace = 0; |
| private long fTime0bak; |
| private long fTime1bak; |
| private ITimeGraphPresentationProvider fTimeGraphProvider = null; |
| private ItemData fItemData = null; |
| private List<SelectionListener> fSelectionListeners; |
| private List<ITimeGraphTimeListener> fDragSelectionListeners; |
| private final List<ISelectionChangedListener> fSelectionChangedListeners = new ArrayList<>(); |
| private final List<ITimeGraphTreeListener> fTreeListeners = new ArrayList<>(); |
| private final List<MenuDetectListener> fTimeGraphEntryMenuListeners = new ArrayList<>(); |
| private final List<MenuDetectListener> fTimeEventMenuListeners = new ArrayList<>(); |
| private final Cursor fDragCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_HAND); |
| private final Cursor fResizeCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_IBEAM); |
| private final Cursor fWaitCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_WAIT); |
| private final Cursor fZoomCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_SIZEWE); |
| private final List<ViewerFilter> fFilters = new ArrayList<>(); |
| private MenuDetectEvent fPendingMenuDetectEvent = null; |
| private boolean fHideArrows = false; |
| private int fAutoExpandLevel = ALL_LEVELS; |
| |
| private int fBorderWidth = 0; |
| private int fHeaderHeight = 0; |
| |
| /** |
| * 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.DOUBLE_BUFFERED); |
| |
| fItemData = new ItemData(); |
| |
| addFocusListener(this); |
| addMouseListener(this); |
| addMouseMoveListener(this); |
| addMouseTrackListener(this); |
| addMouseWheelListener(this); |
| addTraverseListener(this); |
| addKeyListener(this); |
| addMenuDetectListener(this); |
| addListener(SWT.MouseWheel, this); |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| fResourceManager.dispose(); |
| } |
| |
| /** |
| * Sets the timegraph provider used by this timegraph viewer. |
| * |
| * @param timeGraphProvider the timegraph provider |
| */ |
| public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { |
| fTimeGraphProvider = timeGraphProvider; |
| |
| if (timeGraphProvider instanceof ITimeGraphPresentationProvider2) { |
| ((ITimeGraphPresentationProvider2) timeGraphProvider).setDrawingHelper(this); |
| ((ITimeGraphPresentationProvider2) timeGraphProvider).addColorListener(this); |
| } |
| |
| StateItem[] stateItems = fTimeGraphProvider.getStateTable(); |
| colorSettingsChanged(stateItems); |
| } |
| |
| /** |
| * Gets the timegraph provider used by this timegraph viewer. |
| * |
| * @return the timegraph provider, or <code>null</code> if not set. |
| */ |
| public ITimeGraphPresentationProvider getTimeGraphProvider() { |
| return fTimeGraphProvider; |
| } |
| |
| /** |
| * Gets the color map used by this timegraph viewer. |
| * |
| * @return a color map, or <code>null</code> if not set. |
| */ |
| public Color[] getEventColorMap() { |
| return fEventColorMap; |
| } |
| |
| /** |
| * Assign the given time provider |
| * |
| * @param timeProvider |
| * The time provider |
| */ |
| public void setTimeProvider(ITimeDataProvider timeProvider) { |
| fTimeProvider = timeProvider; |
| redraw(); |
| } |
| |
| /** |
| * Assign the status line manager |
| * |
| * @param statusLineManager |
| * The status line manager, or null to disable status line messages |
| */ |
| public void setStatusLineManager(IStatusLineManager statusLineManager) { |
| if (fStatusLineManager != null && statusLineManager == null) { |
| fStatusLineManager.setMessage(""); //$NON-NLS-1$ |
| } |
| fStatusLineManager = statusLineManager; |
| } |
| |
| /** |
| * Assign the time graph scale |
| * |
| * @param timeGraphScale |
| * The time graph scale |
| */ |
| public void setTimeGraphScale(TimeGraphScale timeGraphScale) { |
| fTimeGraphScale = timeGraphScale; |
| } |
| |
| /** |
| * 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 == fSelectionListeners) { |
| fSelectionListeners = new ArrayList<>(); |
| } |
| fSelectionListeners.add(listener); |
| } |
| |
| /** |
| * Remove a selection listener |
| * |
| * @param listener |
| * The listener to remove |
| */ |
| public void removeSelectionListener(SelectionListener listener) { |
| if (null != fSelectionListeners) { |
| fSelectionListeners.remove(listener); |
| } |
| } |
| |
| /** |
| * Selection changed callback |
| */ |
| public void fireSelectionChanged() { |
| if (null != fSelectionListeners) { |
| Iterator<SelectionListener> it = fSelectionListeners.iterator(); |
| while (it.hasNext()) { |
| SelectionListener listener = it.next(); |
| listener.widgetSelected(null); |
| } |
| } |
| } |
| |
| /** |
| * Default selection callback |
| */ |
| public void fireDefaultSelection() { |
| if (null != fSelectionListeners) { |
| Iterator<SelectionListener> it = fSelectionListeners.iterator(); |
| while (it.hasNext()) { |
| SelectionListener listener = it.next(); |
| listener.widgetDefaultSelected(null); |
| } |
| } |
| } |
| |
| /** |
| * Add a drag selection listener |
| * |
| * @param listener |
| * The listener to add |
| */ |
| public void addDragSelectionListener(ITimeGraphTimeListener listener) { |
| if (listener == null) { |
| SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| } |
| if (null == fDragSelectionListeners) { |
| fDragSelectionListeners = new ArrayList<>(); |
| } |
| fDragSelectionListeners.add(listener); |
| } |
| |
| /** |
| * Remove a drag selection listener |
| * |
| * @param listener |
| * The listener to remove |
| */ |
| public void removeDragSelectionListener(ITimeGraphTimeListener listener) { |
| if (null != fDragSelectionListeners) { |
| fDragSelectionListeners.remove(listener); |
| } |
| } |
| |
| /** |
| * Drag Selection changed callback |
| * |
| * @param start |
| * Time interval start |
| * @param end |
| * Time interval end |
| */ |
| public void fireDragSelectionChanged(long start, long end) { |
| // check for backward intervals |
| long beginTime, endTime; |
| if (start > end) { |
| beginTime = end; |
| endTime = start; |
| } else { |
| beginTime = start; |
| endTime = end; |
| } |
| // call the listeners |
| if (null != fDragSelectionListeners) { |
| Iterator<ITimeGraphTimeListener> it = fDragSelectionListeners.iterator(); |
| while (it.hasNext()) { |
| ITimeGraphTimeListener listener = it.next(); |
| listener.timeSelected(new TimeGraphTimeEvent(this, beginTime, endTime)); |
| } |
| } |
| } |
| |
| /** |
| * Get the traces in the model |
| * |
| * @return The array of traces |
| */ |
| public ITimeGraphEntry[] getTraces() { |
| return fItemData.getEntries(); |
| } |
| |
| /** |
| * Get the on/off trace filters |
| * |
| * @return The array of filters |
| */ |
| public boolean[] getTraceFilter() { |
| return fItemData.getEntryFilter(); |
| } |
| |
| /** |
| * Refresh the data for the thing |
| */ |
| public void refreshData() { |
| fItemData.refreshData(); |
| redraw(); |
| } |
| |
| /** |
| * Refresh data for the given traces |
| * |
| * @param traces |
| * The traces to refresh |
| */ |
| public void refreshData(ITimeGraphEntry[] traces) { |
| fItemData.refreshData(traces); |
| redraw(); |
| } |
| |
| /** |
| * Refresh the links (arrows) of this widget |
| * |
| * @param events The link event list |
| */ |
| public void refreshArrows(List<ILinkEvent> events) { |
| fItemData.refreshArrows(events); |
| } |
| |
| /** |
| * Get the links (arrows) of this widget |
| * |
| * @return The unmodifiable link event list |
| * |
| * @since 2.0 |
| */ |
| public List<ILinkEvent> getArrows() { |
| return Collections.unmodifiableList(fItemData.fLinks); |
| } |
| |
| boolean ensureVisibleItem(int idx, boolean redraw) { |
| boolean changed = false; |
| int index = idx; |
| if (index < 0) { |
| for (index = 0; index < fItemData.fExpandedItems.length; index++) { |
| if (fItemData.fExpandedItems[index].fSelected) { |
| break; |
| } |
| } |
| } |
| if (index >= fItemData.fExpandedItems.length) { |
| return changed; |
| } |
| if (index < fTopIndex) { |
| setTopIndex(index); |
| if (redraw) { |
| redraw(); |
| } |
| changed = true; |
| } else { |
| int page = countPerPage(); |
| if (index >= fTopIndex + page) { |
| setTopIndex(index - page + 1); |
| if (redraw) { |
| redraw(); |
| } |
| changed = true; |
| } |
| } |
| return changed; |
| } |
| |
| /** |
| * Assign the given index as the top one |
| * |
| * @param idx |
| * The index |
| */ |
| public void setTopIndex(int idx) { |
| int index = Math.min(idx, fItemData.fExpandedItems.length - countPerPage()); |
| index = Math.max(0, index); |
| fTopIndex = index; |
| redraw(); |
| } |
| |
| /** |
| * Sets the auto-expand level to be used when the entries are refreshed |
| * using {@link #refreshData()} or {@link #refreshData(ITimeGraphEntry[])}. |
| * The value 0 means that there is no auto-expand; 1 means that top-level |
| * entries are expanded, but not their children; 2 means that top-level |
| * entries are expanded, and their children, but not grand-children; and so |
| * on. |
| * <p> |
| * The value {@link #ALL_LEVELS} means that all subtrees should be expanded. |
| * </p> |
| * @param level |
| * non-negative level, or <code>ALL_LEVELS</code> to expand all |
| * levels of the tree |
| */ |
| public void setAutoExpandLevel(int level) { |
| fAutoExpandLevel = level; |
| } |
| |
| /** |
| * Returns the auto-expand level. |
| * |
| * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of |
| * the tree are expanded automatically |
| * @see #setAutoExpandLevel |
| */ |
| public int getAutoExpandLevel() { |
| return fAutoExpandLevel; |
| } |
| |
| /** |
| * 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 = fItemData.findItem(entry); |
| if (item != null && item.fExpanded != expanded) { |
| item.fExpanded = expanded; |
| fItemData.updateExpandedItems(); |
| redraw(); |
| } |
| } |
| |
| /** |
| * Collapses all nodes of the viewer's tree, starting with the root. |
| */ |
| public void collapseAll() { |
| for (Item item : fItemData.fItems) { |
| item.fExpanded = false; |
| } |
| fItemData.updateExpandedItems(); |
| redraw(); |
| } |
| |
| /** |
| * Expands all nodes of the viewer's tree, starting with the root. |
| */ |
| public void expandAll() { |
| for (Item item : fItemData.fItems) { |
| item.fExpanded = true; |
| } |
| fItemData.updateExpandedItems(); |
| redraw(); |
| } |
| |
| /** |
| * Add a tree listener |
| * |
| * @param listener |
| * The listener to add |
| */ |
| public void addTreeListener(ITimeGraphTreeListener listener) { |
| if (!fTreeListeners.contains(listener)) { |
| fTreeListeners.add(listener); |
| } |
| } |
| |
| /** |
| * Remove a tree listener |
| * |
| * @param listener |
| * The listener to remove |
| */ |
| public void removeTreeListener(ITimeGraphTreeListener listener) { |
| if (fTreeListeners.contains(listener)) { |
| fTreeListeners.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 : fTreeListeners) { |
| if (expanded) { |
| listener.treeExpanded(event); |
| } else { |
| listener.treeCollapsed(event); |
| } |
| } |
| } |
| |
| /** |
| * Add a menu listener on {@link ITimeGraphEntry}s |
| * |
| * @param listener |
| * The listener to add |
| */ |
| public void addTimeGraphEntryMenuListener(MenuDetectListener listener) { |
| if (!fTimeGraphEntryMenuListeners.contains(listener)) { |
| fTimeGraphEntryMenuListeners.add(listener); |
| } |
| } |
| |
| /** |
| * Remove a menu listener on {@link ITimeGraphEntry}s |
| * |
| * @param listener |
| * The listener to remove |
| */ |
| public void removeTimeGraphEntryMenuListener(MenuDetectListener listener) { |
| if (fTimeGraphEntryMenuListeners.contains(listener)) { |
| fTimeGraphEntryMenuListeners.remove(listener); |
| } |
| } |
| |
| /** |
| * Menu event callback on {@link ITimeGraphEntry}s |
| * |
| * @param event |
| * The MenuDetectEvent, with field {@link TypedEvent#data} set to the selected {@link ITimeGraphEntry} |
| */ |
| private void fireMenuEventOnTimeGraphEntry(MenuDetectEvent event) { |
| for (MenuDetectListener listener : fTimeGraphEntryMenuListeners) { |
| listener.menuDetected(event); |
| } |
| } |
| |
| /** |
| * Add a menu listener on {@link ITimeEvent}s |
| * |
| * @param listener |
| * The listener to add |
| */ |
| public void addTimeEventMenuListener(MenuDetectListener listener) { |
| if (!fTimeEventMenuListeners.contains(listener)) { |
| fTimeEventMenuListeners.add(listener); |
| } |
| } |
| |
| /** |
| * Remove a menu listener on {@link ITimeEvent}s |
| * |
| * @param listener |
| * The listener to remove |
| */ |
| public void removeTimeEventMenuListener(MenuDetectListener listener) { |
| if (fTimeEventMenuListeners.contains(listener)) { |
| fTimeEventMenuListeners.remove(listener); |
| } |
| } |
| |
| /** |
| * Menu event callback on {@link ITimeEvent}s |
| * |
| * @param event |
| * The MenuDetectEvent, with field {@link TypedEvent#data} set to the selected {@link ITimeEvent} |
| */ |
| private void fireMenuEventOnTimeEvent(MenuDetectEvent event) { |
| for (MenuDetectListener listener : fTimeEventMenuListeners) { |
| listener.menuDetected(event); |
| } |
| } |
| |
| @Override |
| public ISelection getSelection() { |
| TimeGraphSelection sel = new TimeGraphSelection(); |
| ITimeGraphEntry trace = getSelectedTrace(); |
| if (null != trace && null != fTimeProvider) { |
| long selectedTime = fTimeProvider.getSelectionBegin(); |
| 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 < fItemData.fExpandedItems.length; i++) { |
| Item item = fItemData.fExpandedItems[i]; |
| if (item.fSelected) { |
| lastSelection = i; |
| if ((1 == n) && (i < fItemData.fExpandedItems.length - 1)) { |
| item.fSelected = false; |
| item = fItemData.fExpandedItems[i + 1]; |
| item.fSelected = true; |
| changed = true; |
| } else if ((-1 == n) && (i > 0)) { |
| item.fSelected = false; |
| item = fItemData.fExpandedItems[i - 1]; |
| item.fSelected = true; |
| changed = true; |
| } |
| break; |
| } |
| } |
| |
| if (lastSelection < 0 && fItemData.fExpandedItems.length > 0) { |
| Item item = fItemData.fExpandedItems[0]; |
| item.fSelected = true; |
| changed = true; |
| } |
| |
| if (changed) { |
| ensureVisibleItem(-1, false); |
| redraw(); |
| fireSelectionChanged(); |
| } |
| } |
| |
| /** |
| * Select an event |
| * |
| * @param n |
| * 1 for next event, -1 for previous event |
| * @param extend |
| * true to extend selection range, false for single selection |
| * @since 1.0 |
| */ |
| public void selectEvent(int n, boolean extend) { |
| if (null == fTimeProvider) { |
| return; |
| } |
| ITimeGraphEntry trace = getSelectedTrace(); |
| if (trace == null) { |
| return; |
| } |
| long selectedTime = fTimeProvider.getSelectionEnd(); |
| long endTime = fTimeProvider.getMaxTime(); |
| ITimeEvent nextEvent; |
| if (n == -1 && selectedTime > endTime) { |
| nextEvent = Utils.findEvent(trace, selectedTime, 0); |
| } else { |
| nextEvent = Utils.findEvent(trace, selectedTime, n); |
| } |
| if (null == nextEvent && n == -1) { |
| 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 && nextEvent.getTime() + nextEvent.getDuration() < selectedTime) { |
| // for previous event go to its end time unless we were already there |
| nextTime = nextEvent.getTime() + nextEvent.getDuration(); |
| } |
| if (extend) { |
| fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), nextTime); |
| } else { |
| fTimeProvider.setSelectedTimeNotify(nextTime, true); |
| } |
| fireSelectionChanged(); |
| } else if (n == 1) { |
| if (extend) { |
| fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), endTime); |
| } else { |
| fTimeProvider.setSelectedTimeNotify(endTime, true); |
| } |
| fireSelectionChanged(); |
| } |
| updateStatusLine(STATUS_WITHOUT_CURSOR_TIME); |
| } |
| |
| /** |
| * Select the next event |
| * |
| * @param extend |
| * true to extend selection range, false for single selection |
| * @since 1.0 |
| */ |
| public void selectNextEvent(boolean extend) { |
| selectEvent(1, extend); |
| // Notify if visible time window has been adjusted |
| fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1()); |
| } |
| |
| /** |
| * Select the previous event |
| * |
| * @param extend |
| * true to extend selection range, false for single selection |
| * @since 1.0 |
| */ |
| public void selectPrevEvent(boolean extend) { |
| selectEvent(-1, extend); |
| // Notify if visible time window has been adjusted |
| fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1()); |
| } |
| |
| /** |
| * Select the next trace |
| */ |
| public void selectNextTrace() { |
| selectTrace(1); |
| } |
| |
| /** |
| * Select the previous trace |
| */ |
| public void selectPrevTrace() { |
| selectTrace(-1); |
| } |
| |
| /** |
| * Scroll left or right by one half window size |
| * |
| * @param left |
| * true to scroll left, false to scroll right |
| */ |
| public void horizontalScroll(boolean left) { |
| long time0 = fTimeProvider.getTime0(); |
| long time1 = fTimeProvider.getTime1(); |
| long timeMin = fTimeProvider.getMinTime(); |
| long timeMax = fTimeProvider.getMaxTime(); |
| long range = time1 - time0; |
| if (range <= 0) { |
| return; |
| } |
| long increment = Math.max(1, range / 2); |
| if (left) { |
| time0 = Math.max(time0 - increment, timeMin); |
| time1 = time0 + range; |
| } else { |
| time1 = Math.min(time1 + increment, timeMax); |
| time0 = time1 - range; |
| } |
| fTimeProvider.setStartFinishTimeNotify(time0, time1); |
| } |
| |
| /** |
| * 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 = fTimeProvider.getNameSpace(); |
| int timeSpace = fTimeProvider.getTimeSpace(); |
| int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x)); |
| long time0 = fTimeProvider.getTime0(); |
| long time1 = fTimeProvider.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 * ZOOM_IN_FACTOR), fTimeProvider.getMinTimeInterval()); |
| } else { |
| newInterval = (long) Math.ceil(interval * ZOOM_OUT_FACTOR); |
| } |
| long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval)); |
| long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval); |
| long newTime1 = newTime0 + newInterval; |
| fTimeProvider.setStartFinishTimeNotify(newTime0, newTime1); |
| } |
| |
| /** |
| * zoom in using single click |
| */ |
| public void zoomIn() { |
| long prevTime0 = fTimeProvider.getTime0(); |
| long prevTime1 = fTimeProvider.getTime1(); |
| long prevRange = prevTime1 - prevTime0; |
| if (prevRange == 0) { |
| return; |
| } |
| ITimeDataProvider provider = fTimeProvider; |
| long selTime = (provider.getSelectionEnd() + provider.getSelectionBegin()) / 2; |
| if (selTime <= prevTime0 || selTime >= prevTime1) { |
| selTime = (prevTime0 + prevTime1) / 2; |
| } |
| long time0 = selTime - (long) ((selTime - prevTime0) / ZOOM_FACTOR); |
| long time1 = selTime + (long) ((prevTime1 - selTime) / ZOOM_FACTOR); |
| |
| long inaccuracy = (fTimeProvider.getMaxTime() - fTimeProvider.getMinTime()) - (time1 - time0); |
| |
| if (inaccuracy > 0 && inaccuracy < 100) { |
| fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getMinTime(), fTimeProvider.getMaxTime()); |
| return; |
| } |
| |
| long min = fTimeProvider.getMinTimeInterval(); |
| if ((time1 - time0) < min) { |
| time0 = selTime - (selTime - prevTime0) * min / prevRange; |
| time1 = time0 + min; |
| } |
| |
| fTimeProvider.setStartFinishTimeNotify(time0, time1); |
| } |
| |
| /** |
| * zoom out using single click |
| */ |
| public void zoomOut() { |
| long prevTime0 = fTimeProvider.getTime0(); |
| long prevTime1 = fTimeProvider.getTime1(); |
| ITimeDataProvider provider = fTimeProvider; |
| long selTime = (provider.getSelectionEnd() + provider.getSelectionBegin()) / 2; |
| if (selTime <= prevTime0 || selTime >= prevTime1) { |
| selTime = (prevTime0 + prevTime1) / 2; |
| } |
| long time0 = (long) (selTime - (selTime - prevTime0) * ZOOM_FACTOR); |
| long time1 = (long) (selTime + (prevTime1 - selTime) * ZOOM_FACTOR); |
| |
| long inaccuracy = (fTimeProvider.getMaxTime() - fTimeProvider.getMinTime()) - (time1 - time0); |
| if (inaccuracy > 0 && inaccuracy < 100) { |
| fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getMinTime(), fTimeProvider.getMaxTime()); |
| return; |
| } |
| |
| fTimeProvider.setStartFinishTimeNotify(time0, time1); |
| } |
| |
| /** |
| * Hide arrows |
| * |
| * @param hideArrows true to hide arrows |
| */ |
| public void hideArrows(boolean hideArrows) { |
| fHideArrows = hideArrows; |
| } |
| |
| /** |
| * Follow the arrow forward |
| * |
| * @param extend |
| * true to extend selection range, false for single selection |
| * @since 1.0 |
| */ |
| public void followArrowFwd(boolean extend) { |
| ITimeGraphEntry trace = getSelectedTrace(); |
| if (trace == null) { |
| return; |
| } |
| long selectedTime = fTimeProvider.getSelectionEnd(); |
| for (ILinkEvent link : fItemData.fLinks) { |
| if (link.getEntry() == trace && link.getTime() == selectedTime) { |
| selectItem(link.getDestinationEntry(), false); |
| if (link.getDuration() != 0) { |
| if (extend) { |
| fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime() + link.getDuration()); |
| } else { |
| fTimeProvider.setSelectedTimeNotify(link.getTime() + link.getDuration(), true); |
| } |
| // Notify if visible time window has been adjusted |
| fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1()); |
| } |
| fireSelectionChanged(); |
| updateStatusLine(STATUS_WITHOUT_CURSOR_TIME); |
| return; |
| } |
| } |
| selectNextEvent(extend); |
| } |
| |
| /** |
| * Follow the arrow backward |
| * |
| * @param extend |
| * true to extend selection range, false for single selection |
| * @since 1.0 |
| */ |
| public void followArrowBwd(boolean extend) { |
| ITimeGraphEntry trace = getSelectedTrace(); |
| if (trace == null) { |
| return; |
| } |
| long selectedTime = fTimeProvider.getSelectionEnd(); |
| for (ILinkEvent link : fItemData.fLinks) { |
| if (link.getDestinationEntry() == trace && link.getTime() + link.getDuration() == selectedTime) { |
| selectItem(link.getEntry(), false); |
| if (link.getDuration() != 0) { |
| if (extend) { |
| fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime()); |
| } else { |
| fTimeProvider.setSelectedTimeNotify(link.getTime(), true); |
| } |
| // Notify if visible time window has been adjusted |
| fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1()); |
| } |
| fireSelectionChanged(); |
| updateStatusLine(STATUS_WITHOUT_CURSOR_TIME); |
| return; |
| } |
| } |
| selectPrevEvent(extend); |
| } |
| |
| /** |
| * Return the currently selected trace |
| * |
| * @return The entry matching the trace |
| */ |
| public ITimeGraphEntry getSelectedTrace() { |
| ITimeGraphEntry trace = null; |
| int idx = getSelectedIndex(); |
| if (idx >= 0) { |
| trace = fItemData.fExpandedItems[idx].fEntry; |
| } |
| return trace; |
| } |
| |
| /** |
| * Retrieve the index of the currently selected item |
| * |
| * @return The index |
| */ |
| public int getSelectedIndex() { |
| int idx = -1; |
| for (int i = 0; i < fItemData.fExpandedItems.length; i++) { |
| Item item = fItemData.fExpandedItems[i]; |
| if (item.fSelected) { |
| idx = i; |
| break; |
| } |
| } |
| return idx; |
| } |
| |
| boolean toggle(int idx) { |
| boolean toggled = false; |
| if (idx >= 0 && idx < fItemData.fExpandedItems.length) { |
| Item item = fItemData.fExpandedItems[idx]; |
| if (item.fHasChildren) { |
| item.fExpanded = !item.fExpanded; |
| fItemData.updateExpandedItems(); |
| redraw(); |
| toggled = true; |
| fireTreeEvent(item.fEntry, item.fExpanded); |
| } |
| } |
| return toggled; |
| } |
| |
| /** |
| * Gets the index of the item at the given location. |
| * |
| * @param y |
| * the y coordinate |
| * @return the index of the item at the given location, of -1 if none. |
| */ |
| protected int getItemIndexAtY(int y) { |
| if (y < 0) { |
| return -1; |
| } |
| int ySum = 0; |
| for (int idx = fTopIndex; idx < fItemData.fExpandedItems.length; idx++) { |
| ySum += fItemData.fExpandedItems[idx].fItemHeight; |
| if (y < ySum) { |
| return idx; |
| } |
| } |
| return -1; |
| } |
| |
| boolean isOverSplitLine(int x) { |
| if (x < 0 || null == fTimeProvider) { |
| return false; |
| } |
| int nameWidth = fTimeProvider.getNameSpace(); |
| return Math.abs(x - nameWidth) < SNAP_WIDTH; |
| } |
| |
| /** |
| * Gets the {@link ITimeGraphEntry} at the given location. |
| * |
| * @param pt |
| * a point in the widget |
| * @return the {@link ITimeGraphEntry} at this point, or <code>null</code> |
| * if none. |
| */ |
| protected ITimeGraphEntry getEntry(Point pt) { |
| int idx = getItemIndexAtY(pt.y); |
| return idx >= 0 ? fItemData.fExpandedItems[idx].fEntry : null; |
| } |
| |
| /** |
| * Return the arrow event closest to the given point that is no further than |
| * a maximum distance. |
| * |
| * @param pt |
| * a point in the widget |
| * @return The closest arrow event, or null if there is none close enough. |
| */ |
| protected ILinkEvent getArrow(Point pt) { |
| if (fHideArrows) { |
| return null; |
| } |
| ILinkEvent linkEvent = null; |
| double minDistance = Double.MAX_VALUE; |
| for (ILinkEvent event : fItemData.fLinks) { |
| Rectangle rect = getArrowRectangle(new Rectangle(0, 0, 0, 0), event); |
| if (rect != null) { |
| int x1 = rect.x; |
| int y1 = rect.y; |
| int x2 = x1 + rect.width; |
| int y2 = y1 + rect.height; |
| double d = Utils.distance(pt.x, pt.y, x1, y1, x2, y2); |
| if (minDistance > d) { |
| minDistance = d; |
| linkEvent = event; |
| } |
| } |
| } |
| if (minDistance <= ARROW_HOVER_MAX_DIST) { |
| return linkEvent; |
| } |
| return null; |
| } |
| |
| @Override |
| public int getXForTime(long time) { |
| if (null == fTimeProvider) { |
| return -1; |
| } |
| long time0 = fTimeProvider.getTime0(); |
| long time1 = fTimeProvider.getTime1(); |
| int width = getSize().x; |
| int nameSpace = fTimeProvider.getNameSpace(); |
| double pixelsPerNanoSec = (width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (width - nameSpace - RIGHT_MARGIN) / (time1 - time0); |
| int x = getBounds().x + nameSpace + (int) ((time - time0) * pixelsPerNanoSec); |
| return x; |
| } |
| |
| @Override |
| public long getTimeAtX(int coord) { |
| if (null == fTimeProvider) { |
| return -1; |
| } |
| long hitTime = -1; |
| Point size = getSize(); |
| long time0 = fTimeProvider.getTime0(); |
| long time1 = fTimeProvider.getTime1(); |
| int nameWidth = fTimeProvider.getNameSpace(); |
| final int x = coord - 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 < fItemData.fExpandedItems.length) { |
| Item item = fItemData.fExpandedItems[idx]; |
| changed = !item.fSelected; |
| item.fSelected = true; |
| } |
| } else { |
| for (int i = 0; i < fItemData.fExpandedItems.length; i++) { |
| Item item = fItemData.fExpandedItems[i]; |
| if ((i == idx && !item.fSelected) || (idx == -1 && item.fSelected)) { |
| changed = true; |
| } |
| item.fSelected = 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 = fItemData.findItemIndex(trace); |
| selectItem(idx, addSelection); |
| } |
| |
| /** |
| * Retrieve the number of entries shown per page. |
| * |
| * @return The count |
| */ |
| public int countPerPage() { |
| int height = getSize().y; |
| int count = 0; |
| int ySum = 0; |
| for (int idx = fTopIndex; idx < fItemData.fExpandedItems.length; idx++) { |
| ySum += fItemData.fExpandedItems[idx].fItemHeight; |
| if (ySum >= height) { |
| return count; |
| } |
| count++; |
| } |
| for (int idx = fTopIndex - 1; idx >= 0; idx--) { |
| ySum += fItemData.fExpandedItems[idx].fItemHeight; |
| if (ySum >= height) { |
| return count; |
| } |
| count++; |
| } |
| return count; |
| } |
| |
| /** |
| * Get the index of the top element |
| * |
| * @return The index |
| */ |
| public int getTopIndex() { |
| return fTopIndex; |
| } |
| |
| /** |
| * Get the number of expanded items |
| * |
| * @return The count of expanded items |
| */ |
| public int getExpandedElementCount() { |
| return fItemData.fExpandedItems.length; |
| } |
| |
| /** |
| * Get an array of all expanded elements |
| * |
| * @return The expanded elements |
| */ |
| public ITimeGraphEntry[] getExpandedElements() { |
| ArrayList<ITimeGraphEntry> elements = new ArrayList<>(); |
| for (Item item : fItemData.fExpandedItems) { |
| elements.add(item.fEntry); |
| } |
| return elements.toArray(new ITimeGraphEntry[0]); |
| } |
| |
| Rectangle getNameRect(Rectangle bound, int idx, int nameWidth) { |
| Rectangle rect = getStatesRect(bound, idx, nameWidth); |
| rect.x = bound.x; |
| rect.width = nameWidth; |
| return rect; |
| } |
| |
| Rectangle getStatesRect(Rectangle bound, int idx, int nameWidth) { |
| int x = bound.x + nameWidth; |
| int width = bound.width - x; |
| int ySum = 0; |
| if (idx >= fTopIndex) { |
| for (int i = fTopIndex; i < idx; i++) { |
| ySum += fItemData.fExpandedItems[i].fItemHeight; |
| } |
| } else { |
| for (int i = fTopIndex - 1; i >= idx; i--) { |
| ySum -= fItemData.fExpandedItems[i].fItemHeight; |
| } |
| } |
| int y = bound.y + ySum; |
| int height = fItemData.fExpandedItems[idx].fItemHeight; |
| return new Rectangle(x, y, width, height); |
| } |
| |
| @Override |
| void paint(Rectangle bounds, PaintEvent e) { |
| GC gc = e.gc; |
| gc.setBackground(getColorScheme().getColor(TimeGraphColorScheme.BACKGROUND)); |
| drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height); |
| |
| if (bounds.width < 2 || bounds.height < 2 || null == fTimeProvider) { |
| return; |
| } |
| |
| fIdealNameSpace = 0; |
| int nameSpace = fTimeProvider.getNameSpace(); |
| |
| // draw empty name space background |
| gc.setBackground(getColorScheme().getBkColor(false, false, true)); |
| drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height); |
| |
| // draw items |
| drawItems(bounds, fTimeProvider, fItemData.fExpandedItems, fTopIndex, nameSpace, gc); |
| drawLinks(bounds, fTimeProvider, fItemData.fLinks, nameSpace, gc); |
| fTimeGraphProvider.postDrawControl(bounds, gc); |
| |
| int alpha = gc.getAlpha(); |
| gc.setAlpha(100); |
| |
| long time0 = fTimeProvider.getTime0(); |
| long time1 = fTimeProvider.getTime1(); |
| long selectionBegin = fTimeProvider.getSelectionBegin(); |
| long selectionEnd = fTimeProvider.getSelectionEnd(); |
| double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0); |
| int x0 = bounds.x + nameSpace + (int) ((selectionBegin - time0) * pixelsPerNanoSec); |
| int x1 = bounds.x + nameSpace + (int) ((selectionEnd - time0) * pixelsPerNanoSec); |
| |
| // draw selection lines |
| if (fDragState != DRAG_SELECTION) { |
| gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME)); |
| if (x0 >= nameSpace && x0 < bounds.x + bounds.width) { |
| gc.drawLine(x0, bounds.y, x0, bounds.y + bounds.height); |
| } |
| if (x1 != x0) { |
| if (x1 >= nameSpace && x1 < bounds.x + bounds.width) { |
| gc.drawLine(x1, bounds.y, x1, bounds.y + bounds.height); |
| } |
| } |
| } |
| |
| // draw selection background |
| if (selectionBegin != 0 && selectionEnd != 0 && fDragState != DRAG_SELECTION) { |
| x0 = Math.max(nameSpace, Math.min(bounds.x + bounds.width, x0)); |
| x1 = Math.max(nameSpace, Math.min(bounds.x + bounds.width, x1)); |
| gc.setBackground(getColorScheme().getBkColor(false, false, true)); |
| if (x1 - x0 > 1) { |
| gc.fillRectangle(new Rectangle(x0 + 1, bounds.y, x1 - x0 - 1, bounds.height)); |
| } else if (x0 - x1 > 1) { |
| gc.fillRectangle(new Rectangle(x1 + 1, bounds.y, x0 - x1 - 1, bounds.height)); |
| } |
| } |
| |
| // draw drag selection background |
| if (fDragState == DRAG_ZOOM || fDragState == DRAG_SELECTION) { |
| gc.setBackground(getColorScheme().getBkColor(false, false, true)); |
| if (fDragX0 < fDragX) { |
| gc.fillRectangle(new Rectangle(fDragX0, bounds.y, fDragX - fDragX0, bounds.height)); |
| } else if (fDragX0 > fDragX) { |
| gc.fillRectangle(new Rectangle(fDragX, bounds.y, fDragX0 - fDragX, bounds.height)); |
| } |
| } |
| |
| // draw drag line |
| if (DRAG_SPLIT_LINE == fDragState) { |
| gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.BLACK)); |
| gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1); |
| } else if (DRAG_ZOOM == fDragState && Math.max(fDragX, fDragX0) > nameSpace) { |
| gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_FOREGROUND)); |
| gc.drawLine(fDragX0, bounds.y, fDragX0, bounds.y + bounds.height - 1); |
| if (fDragX != fDragX0) { |
| gc.drawLine(fDragX, bounds.y, fDragX, bounds.y + bounds.height - 1); |
| } |
| } else if (DRAG_SELECTION == fDragState && Math.max(fDragX, fDragX0) > nameSpace) { |
| gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME)); |
| gc.drawLine(fDragX0, bounds.y, fDragX0, bounds.y + bounds.height - 1); |
| if (fDragX != fDragX0) { |
| gc.drawLine(fDragX, bounds.y, fDragX, bounds.y + bounds.height - 1); |
| } |
| } else if (DRAG_NONE == fDragState && fMouseOverSplitLine && fTimeProvider.getNameSpace() > 0) { |
| gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.RED)); |
| gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1); |
| } |
| |
| gc.setAlpha(alpha); |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| |
| /** |
| * Draws the item |
| * |
| * @param item the item to draw |
| * @param bounds the container rectangle |
| * @param timeProvider Time provider |
| * @param i the item index |
| * @param nameSpace the name space |
| * @param gc Graphics context |
| */ |
| protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) { |
| ITimeGraphEntry entry = item.fEntry; |
| long time0 = timeProvider.getTime0(); |
| long time1 = timeProvider.getTime1(); |
| long selectedTime = fTimeProvider.getSelectionEnd(); |
| |
| Rectangle nameRect = getNameRect(bounds, i, nameSpace); |
| if (nameRect.y >= bounds.y + bounds.height) { |
| return; |
| } |
| |
| if (! item.fEntry.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(getColorScheme().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.fSelected; |
| // K pixels per second |
| double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0); |
| |
| if (item.fEntry.hasTimeEvents()) { |
| gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height)); |
| 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<ITimeEvent> 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; |
| } |
| } |
| boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration(); |
| if (drawState(getColorScheme(), event, stateRect, gc, selected, timeSelected)) { |
| lastX = stateRect.x; |
| } |
| } |
| gc.setClipping((Rectangle) null); |
| } |
| fTimeGraphProvider.postDrawEntry(entry, rect, gc); |
| } |
| |
| /** |
| * Draw the links |
| * |
| * @param bounds |
| * The rectangle of the area |
| * @param timeProvider |
| * The time provider |
| * @param links |
| * The array items to draw |
| * @param nameSpace |
| * The width reserved for the names |
| * @param gc |
| * Reference to the SWT GC object |
| */ |
| public void drawLinks(Rectangle bounds, ITimeDataProvider timeProvider, |
| List<ILinkEvent> links, int nameSpace, GC gc) { |
| if (fHideArrows) { |
| return; |
| } |
| gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height)); |
| for (ILinkEvent event : links) { |
| drawLink(event, bounds, timeProvider, nameSpace, gc); |
| } |
| gc.setClipping((Rectangle) null); |
| } |
| |
| /** |
| * Draws the link type events of this item |
| * |
| * @param event |
| * the item to draw |
| * @param bounds |
| * the container rectangle |
| * @param timeProvider |
| * Time provider |
| * @param nameSpace |
| * the name space |
| * @param gc |
| * Graphics context |
| */ |
| protected void drawLink(ILinkEvent event, Rectangle bounds, ITimeDataProvider timeProvider, int nameSpace, GC gc) { |
| drawArrow(getColorScheme(), event, getArrowRectangle(bounds, event), gc); |
| } |
| |
| private Rectangle getArrowRectangle(Rectangle bounds, ILinkEvent event) { |
| int srcIndex = fItemData.findItemIndex(event.getEntry()); |
| int destIndex = fItemData.findItemIndex(event.getDestinationEntry()); |
| |
| if ((srcIndex == -1) || (destIndex == -1)) { |
| return null; |
| } |
| |
| Rectangle src = getStatesRect(bounds, srcIndex, fTimeProvider.getNameSpace()); |
| Rectangle dst = getStatesRect(bounds, destIndex, fTimeProvider.getNameSpace()); |
| |
| int x0 = getXForTime(event.getTime()); |
| int x1 = getXForTime(event.getTime() + event.getDuration()); |
| |
| // limit the x-coordinates to prevent integer overflow in calculations |
| // and also GC.drawLine doesn't draw properly with large coordinates |
| final int limit = Integer.MAX_VALUE / 1024; |
| x0 = Math.max(-limit, Math.min(x0, limit)); |
| x1 = Math.max(-limit, Math.min(x1, limit)); |
| |
| int y0 = src.y + src.height / 2; |
| int y1 = dst.y + dst.height / 2; |
| return new Rectangle(x0, y0, x1 - x0, y1 - y0); |
| } |
| |
| /** |
| * Draw the state (color fill) |
| * |
| * @param colors |
| * Color scheme |
| * @param event |
| * Time event for which we're drawing the state |
| * @param rect |
| * Where to draw |
| * @param gc |
| * Graphics context |
| * @return true if the state was drawn |
| */ |
| protected boolean drawArrow(TimeGraphColorScheme colors, ITimeEvent event, |
| Rectangle rect, GC gc) { |
| |
| if (rect == null) { |
| return false; |
| } |
| int colorIdx = fTimeGraphProvider.getStateTableIndex(event); |
| if (colorIdx < 0) { |
| return false; |
| } |
| boolean visible = ((rect.height == 0) && (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); |
| } |
| |
| gc.setForeground(stateColor); |
| gc.setBackground(stateColor); |
| |
| /* Draw the arrow */ |
| gc.drawLine(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); |
| drawArrowHead(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, gc); |
| |
| } |
| fTimeGraphProvider.postDrawEvent(event, rect, gc); |
| return visible; |
| } |
| |
| /* |
| * @author Francis Giraldeau |
| * |
| * Inspiration: |
| * http://stackoverflow.com/questions/3010803/draw-arrow-on-line-algorithm |
| * |
| * The algorithm was taken from this site, not the code itself |
| */ |
| private static void drawArrowHead(int x0, int y0, int x1, int y1, GC gc) |
| { |
| int factor = 10; |
| double cos = 0.9510; |
| double sin = 0.3090; |
| long lenx = x1 - x0; |
| long leny = y1 - y0; |
| double len = Math.sqrt(lenx * lenx + leny * leny); |
| |
| double dx = factor * lenx / len; |
| double dy = factor * leny / len; |
| int end1X = (int) Math.round((x1 - (dx * cos + dy * -sin))); |
| int end1Y = (int) Math.round((y1 - (dx * sin + dy * cos))); |
| int end2X = (int) Math.round((x1 - (dx * cos + dy * sin))); |
| int end2Y = (int) Math.round((y1 - (dx * -sin + dy * cos))); |
| int[] arrow = new int[] { x1, y1, end1X, end1Y, end2X, end2Y, x1, y1 }; |
| gc.fillPolygon(arrow); |
| } |
| |
| /** |
| * Draw the name of an item. |
| * |
| * @param item |
| * Item object |
| * @param bounds |
| * Where to draw the name |
| * @param gc |
| * Graphics context |
| */ |
| protected void drawName(Item item, Rectangle bounds, GC gc) { |
| boolean hasTimeEvents = item.fEntry.hasTimeEvents(); |
| if (! hasTimeEvents) { |
| gc.setBackground(getColorScheme().getBkColorGroup(item.fSelected, fIsInFocus)); |
| gc.fillRectangle(bounds); |
| if (item.fSelected && fIsInFocus) { |
| gc.setForeground(getColorScheme().getBkColor(item.fSelected, fIsInFocus, false)); |
| gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1); |
| } |
| } else { |
| gc.setBackground(getColorScheme().getBkColor(item.fSelected, fIsInFocus, true)); |
| gc.setForeground(getColorScheme().getFgColor(item.fSelected, fIsInFocus)); |
| gc.fillRectangle(bounds); |
| } |
| |
| // No name to be drawn |
| if (fTimeProvider.getNameSpace() == 0) { |
| return; |
| } |
| |
| int leftMargin = MARGIN + item.fLevel * EXPAND_SIZE; |
| if (item.fHasChildren) { |
| gc.setForeground(getColorScheme().getFgColorGroup(false, false)); |
| gc.setBackground(getColorScheme().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.fExpanded) { |
| 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.fEntry); |
| 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.fName; |
| Point size = gc.stringExtent(name); |
| if (fIdealNameSpace < leftMargin + size.x + MARGIN) { |
| fIdealNameSpace = 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(getColorScheme().getFgColor(item.fSelected, fIsInFocus)); |
| 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(getColorScheme().getColor(TimeGraphColorScheme.MID_LINE)); |
| gc.drawLine(x, midy, x + width, midy); |
| } |
| } |
| } |
| |
| /** |
| * Draw the state (color fill) |
| * |
| * @param colors |
| * Color scheme |
| * @param event |
| * Time event for which we're drawing the state |
| * @param rect |
| * Where to draw |
| * @param gc |
| * Graphics context |
| * @param selected |
| * Is this time event currently selected (so it appears |
| * highlighted) |
| * @param timeSelected |
| * Is the timestamp currently selected |
| * @return true if the state was drawn |
| */ |
| protected boolean drawState(TimeGraphColorScheme colors, ITimeEvent event, |
| Rectangle rect, GC gc, boolean selected, boolean timeSelected) { |
| |
| int colorIdx = fTimeGraphProvider.getStateTableIndex(event); |
| if (colorIdx < 0 && colorIdx != ITimeGraphPresentationProvider.TRANSPARENT) { |
| return false; |
| } |
| boolean visible = rect.width == 0 ? false : true; |
| rect.width = Math.max(1, rect.width); |
| Color black = Display.getDefault().getSystemColor(SWT.COLOR_BLACK); |
| gc.setForeground(black); |
| |
| if (colorIdx == ITimeGraphPresentationProvider.TRANSPARENT) { |
| if (visible) { |
| // Only draw the top and bottom borders |
| gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y); |
| gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1); |
| if (rect.width == 1) { |
| gc.drawPoint(rect.x, rect.y - 2); |
| } |
| } |
| fTimeGraphProvider.postDrawEvent(event, rect, gc); |
| return false; |
| } |
| Color stateColor = null; |
| if (colorIdx < fEventColorMap.length) { |
| stateColor = fEventColorMap[colorIdx]; |
| } else { |
| stateColor = black; |
| } |
| |
| boolean reallySelected = timeSelected && selected; |
| // fill all rect area |
| gc.setBackground(stateColor); |
| if (visible) { |
| gc.fillRectangle(rect); |
| } else if (fBlendSubPixelEvents) { |
| gc.setAlpha(128); |
| gc.fillRectangle(rect); |
| gc.setAlpha(255); |
| } |
| |
| if (reallySelected) { |
| gc.drawLine(rect.x, rect.y - 1, rect.x + rect.width - 1, rect.y - 1); |
| gc.drawLine(rect.x, rect.y + rect.height, rect.x + rect.width - 1, rect.y + rect.height); |
| } |
| if (!visible) { |
| gc.drawPoint(rect.x, rect.y - 2); |
| } |
| fTimeGraphProvider.postDrawEvent(event, rect, gc); |
| return visible; |
| } |
| |
| /** |
| * Fill the space between two contiguous time events |
| * |
| * @param rect |
| * Rectangle to fill |
| * @param gc |
| * Graphics context |
| * @param selected |
| * Is this time event selected or not |
| */ |
| protected void fillSpace(Rectangle rect, GC gc, boolean selected) { |
| gc.setBackground(getColorScheme().getBkColor(selected, fIsInFocus, false)); |
| gc.fillRectangle(rect); |
| if (fDragState == DRAG_ZOOM) { |
| gc.setBackground(getColorScheme().getBkColor(selected, fIsInFocus, true)); |
| if (fDragX0 < fDragX) { |
| gc.fillRectangle(new Rectangle(fDragX0, rect.y, fDragX - fDragX0, rect.height)); |
| } else if (fDragX0 > fDragX) { |
| gc.fillRectangle(new Rectangle(fDragX, rect.y, fDragX0 - fDragX, rect.height)); |
| } |
| } |
| // draw middle line |
| gc.setForeground(getColorScheme().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 (fItemData.fExpandedItems.length == 0) { |
| return; |
| } |
| if (SWT.HOME == e.keyCode) { |
| idx = 0; |
| } else if (SWT.END == e.keyCode) { |
| idx = fItemData.fExpandedItems.length - 1; |
| } else if (SWT.ARROW_DOWN == e.keyCode) { |
| idx = getSelectedIndex(); |
| if (idx < 0) { |
| idx = 0; |
| } else if (idx < fItemData.fExpandedItems.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 && fDragState == DRAG_NONE) { |
| boolean extend = (e.stateMask & SWT.SHIFT) != 0; |
| selectPrevEvent(extend); |
| } else if (SWT.ARROW_RIGHT == e.keyCode && fDragState == DRAG_NONE) { |
| boolean extend = (e.stateMask & SWT.SHIFT) != 0; |
| System.out.println("ARROW_RIGHT before ("+extend+") "+fTimeProvider.getSelectionBegin()+"-"+fTimeProvider.getSelectionEnd()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| selectNextEvent(extend); |
| System.out.println("ARROW_RIGHT after ("+extend+") "+fTimeProvider.getSelectionBegin()+"-"+fTimeProvider.getSelectionEnd()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } else if (SWT.PAGE_DOWN == e.keyCode) { |
| int page = countPerPage(); |
| idx = getSelectedIndex(); |
| if (idx < 0) { |
| idx = 0; |
| } |
| idx += page; |
| if (idx >= fItemData.fExpandedItems.length) { |
| idx = fItemData.fExpandedItems.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 (fItemData.fExpandedItems[idx].fHasChildren) { |
| toggle(idx); |
| } else { |
| fireDefaultSelection(); |
| } |
| } |
| idx = -1; |
| } |
| if (idx >= 0) { |
| selectItem(idx, false); |
| fireSelectionChanged(); |
| } |
| int x = toControl(e.display.getCursorLocation()).x; |
| updateCursor(x, e.stateMask | e.keyCode); |
| } |
| |
| @Override |
| public void keyReleased(KeyEvent e) { |
| int x = toControl(e.display.getCursorLocation()).x; |
| updateCursor(x, e.stateMask & ~e.keyCode); |
| } |
| |
| @Override |
| public void focusGained(FocusEvent e) { |
| fIsInFocus = true; |
| redraw(); |
| updateStatusLine(STATUS_WITHOUT_CURSOR_TIME); |
| } |
| |
| @Override |
| public void focusLost(FocusEvent e) { |
| fIsInFocus = false; |
| if (DRAG_NONE != fDragState) { |
| setCapture(false); |
| fDragState = DRAG_NONE; |
| } |
| redraw(); |
| updateStatusLine(NO_STATUS); |
| } |
| |
| /** |
| * @return If the current view is focused |
| */ |
| public boolean isInFocus() { |
| return fIsInFocus; |
| } |
| |
| /** |
| * 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(fWaitCursor); |
| } else { |
| setCursor(null); |
| } |
| } |
| |
| private void updateCursor(int x, int stateMask) { |
| // if Wait cursor not active, check for the need to change the cursor |
| if (getCursor() == fWaitCursor) { |
| return; |
| } |
| Cursor cursor = null; |
| if (fDragState == DRAG_SPLIT_LINE) { |
| } else if (fDragState == DRAG_SELECTION) { |
| cursor = fResizeCursor; |
| } else if (fDragState == DRAG_TRACE_ITEM) { |
| cursor = fDragCursor; |
| } else if (fDragState == DRAG_ZOOM) { |
| cursor = fZoomCursor; |
| } else if ((stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) { |
| cursor = fDragCursor; |
| } else if ((stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) { |
| cursor = fResizeCursor; |
| } else if (!isOverSplitLine(x)) { |
| long selectionBegin = fTimeProvider.getSelectionBegin(); |
| long selectionEnd = fTimeProvider.getSelectionEnd(); |
| int xBegin = getXForTime(selectionBegin); |
| int xEnd = getXForTime(selectionEnd); |
| if (Math.abs(x - xBegin) < SNAP_WIDTH || Math.abs(x - xEnd) < SNAP_WIDTH) { |
| cursor = fResizeCursor; |
| } |
| } |
| if (getCursor() != cursor) { |
| setCursor(cursor); |
| } |
| } |
| |
| private void updateStatusLine(int x) { |
| // use the time provider of the time graph scale for the status line |
| ITimeDataProvider tdp = fTimeGraphScale.getTimeProvider(); |
| if (fStatusLineManager == null || null == tdp || |
| tdp.getTime0() == tdp.getTime1()) { |
| return; |
| } |
| TimeFormat tf = tdp.getTimeFormat(); |
| Resolution res = Resolution.NANOSEC; |
| StringBuilder message = new StringBuilder(); |
| if ((x >= 0 || x == STATUS_WITHOUT_CURSOR_TIME) && fDragState == DRAG_NONE) { |
| if (x != STATUS_WITHOUT_CURSOR_TIME) { |
| long time = getTimeAtX(x); |
| if (time >= 0) { |
| if (tdp instanceof ITimeDataProviderConverter) { |
| time = ((ITimeDataProviderConverter) tdp).convertTime(time); |
| } |
| message.append(NLS.bind("T: {0}{1} ", //$NON-NLS-1$ |
| new Object[] { |
| tf == TimeFormat.CALENDAR ? Utils.formatDate(time) + ' ' : "", //$NON-NLS-1$ |
| Utils.formatTime(time, tf, res) |
| })); |
| } |
| } |
| long selectionBegin = tdp.getSelectionBegin(); |
| long selectionEnd = tdp.getSelectionEnd(); |
| message.append(NLS.bind("T1: {0}{1}", //$NON-NLS-1$ |
| new Object[] { |
| tf == TimeFormat.CALENDAR ? Utils.formatDate(selectionBegin) + ' ' : "", //$NON-NLS-1$ |
| Utils.formatTime(selectionBegin, tf, res) |
| })); |
| if (selectionBegin != selectionEnd) { |
| message.append(NLS.bind(" T2: {0}{1} \u0394: {2}", //$NON-NLS-1$ |
| new Object[] { |
| tf == TimeFormat.CALENDAR ? Utils.formatDate(selectionEnd) + ' ' : "", //$NON-NLS-1$ |
| Utils.formatTime(selectionEnd, tf, res), |
| Utils.formatDelta(selectionEnd - selectionBegin, tf, res) |
| })); |
| } |
| } else if (fDragState == DRAG_SELECTION || fDragState == DRAG_ZOOM) { |
| long time0 = fDragBeginMarker ? getTimeAtX(fDragX0) : fDragTime0; |
| long time = fDragBeginMarker ? fDragTime0 : getTimeAtX(fDragX); |
| if (tdp instanceof ITimeDataProviderConverter) { |
| time0 = ((ITimeDataProviderConverter) tdp).convertTime(time0); |
| time = ((ITimeDataProviderConverter) tdp).convertTime(time); |
| } |
| message.append(NLS.bind("T1: {0}{1} T2: {2}{3} \u0394: {4}", //$NON-NLS-1$ |
| new Object[] { |
| tf == TimeFormat.CALENDAR ? Utils.formatDate(time0) + ' ' : "", //$NON-NLS-1$ |
| Utils.formatTime(time0, tf, res), |
| tf == TimeFormat.CALENDAR ? Utils.formatDate(time) + ' ' : "", //$NON-NLS-1$ |
| Utils.formatTime(time, tf, res), |
| Utils.formatDelta(time - time0, tf, res) |
| })); |
| } |
| fStatusLineManager.setMessage(message.toString()); |
| } |
| |
| @Override |
| public void mouseMove(MouseEvent e) { |
| if (null == fTimeProvider) { |
| return; |
| } |
| Point size = getSize(); |
| if (DRAG_TRACE_ITEM == fDragState) { |
| int nameWidth = fTimeProvider.getNameSpace(); |
| if (e.x > nameWidth && size.x > nameWidth && fDragX != e.x) { |
| fDragX = e.x; |
| double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (fTime1bak - fTime0bak); |
| long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((fDragX - fDragX0) / pixelsPerNanoSec)); |
| long time1 = fTime1bak - timeDelta; |
| long maxTime = fTimeProvider.getMaxTime(); |
| if (time1 > maxTime) { |
| time1 = maxTime; |
| } |
| long time0 = time1 - (fTime1bak - fTime0bak); |
| if (time0 < fTimeProvider.getMinTime()) { |
| time0 = fTimeProvider.getMinTime(); |
| time1 = time0 + (fTime1bak - fTime0bak); |
| } |
| fTimeProvider.setStartFinishTimeNotify(time0, time1); |
| } |
| } else if (DRAG_SPLIT_LINE == fDragState) { |
| fDragX = e.x; |
| fTimeProvider.setNameSpace(e.x); |
| TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, getTimeViewAlignmentInfo())); |
| } else if (DRAG_SELECTION == fDragState) { |
| if (fDragBeginMarker) { |
| fDragX0 = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN); |
| } else { |
| fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN); |
| } |
| redraw(); |
| fTimeGraphScale.setDragRange(fDragX0, fDragX); |
| fireDragSelectionChanged(getTimeAtX(fDragX0), getTimeAtX(fDragX)); |
| } else if (DRAG_ZOOM == fDragState) { |
| fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN); |
| redraw(); |
| fTimeGraphScale.setDragRange(fDragX0, fDragX); |
| } else if (DRAG_NONE == fDragState) { |
| boolean mouseOverSplitLine = isOverSplitLine(e.x); |
| if (fMouseOverSplitLine != mouseOverSplitLine) { |
| redraw(); |
| } |
| fMouseOverSplitLine = mouseOverSplitLine; |
| } |
| updateCursor(e.x, e.stateMask); |
| updateStatusLine(e.x); |
| } |
| |
| @Override |
| public void mouseDoubleClick(MouseEvent e) { |
| if (null == fTimeProvider) { |
| return; |
| } |
| if (1 == e.button && (e.stateMask & SWT.BUTTON_MASK) == 0) { |
| if (isOverSplitLine(e.x) && fTimeProvider.getNameSpace() != 0) { |
| fTimeProvider.setNameSpace(fIdealNameSpace); |
| boolean mouseOverSplitLine = isOverSplitLine(e.x); |
| if (fMouseOverSplitLine != mouseOverSplitLine) { |
| redraw(); |
| } |
| fMouseOverSplitLine = mouseOverSplitLine; |
| TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, getTimeViewAlignmentInfo())); |
| return; |
| } |
| int idx = getItemIndexAtY(e.y); |
| if (idx >= 0) { |
| selectItem(idx, false); |
| fireDefaultSelection(); |
| } |
| } |
| } |
| |
| @Override |
| public void mouseDown(MouseEvent e) { |
| if (fDragState != DRAG_NONE || null == fTimeProvider || |
| fTimeProvider.getTime0() == fTimeProvider.getTime1() || |
| getSize().x - fTimeProvider.getNameSpace() <= 0) { |
| return; |
| } |
| int idx; |
| if (1 == e.button && (e.stateMask & SWT.MODIFIER_MASK) == 0) { |
| int nameSpace = fTimeProvider.getNameSpace(); |
| if (nameSpace != 0 && isOverSplitLine(e.x)) { |
| fDragState = DRAG_SPLIT_LINE; |
| fDragButton = e.button; |
| fDragX = e.x; |
| fDragX0 = fDragX; |
| fTime0bak = fTimeProvider.getTime0(); |
| fTime1bak = fTimeProvider.getTime1(); |
| redraw(); |
| updateCursor(e.x, e.stateMask); |
| return; |
| } |
| } |
| if (1 == e.button && ((e.stateMask & SWT.MODIFIER_MASK) == 0 || (e.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT)) { |
| int nameSpace = fTimeProvider.getNameSpace(); |
| idx = getItemIndexAtY(e.y); |
| if (idx >= 0) { |
| Item item = fItemData.fExpandedItems[idx]; |
| if (item.fHasChildren && e.x < nameSpace && e.x < MARGIN + (item.fLevel + 1) * EXPAND_SIZE) { |
| toggle(idx); |
| return; |
| } |
| selectItem(idx, false); |
| fireSelectionChanged(); |
| } else { |
| selectItem(idx, false); // clear selection |
| fireSelectionChanged(); |
| } |
| long hitTime = getTimeAtX(e.x); |
| if (hitTime >= 0) { |
| setCapture(true); |
| |
| fDragState = DRAG_SELECTION; |
| fDragBeginMarker = false; |
| fDragButton = e.button; |
| fDragX = e.x; |
| fDragX0 = fDragX; |
| fDragTime0 = getTimeAtX(fDragX0); |
| long selectionBegin = fTimeProvider.getSelectionBegin(); |
| long selectionEnd = fTimeProvider.getSelectionEnd(); |
| int xBegin = getXForTime(selectionBegin); |
| int xEnd = getXForTime(selectionEnd); |
| if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) { |
| long time = getTimeAtX(e.x); |
| if (Math.abs(time - selectionBegin) < Math.abs(time - selectionEnd)) { |
| fDragBeginMarker = true; |
| fDragX = xEnd; |
| fDragX0 = e.x; |
| fDragTime0 = selectionEnd; |
| } else { |
| fDragX0 = xBegin; |
| fDragTime0 = selectionBegin; |
| } |
| } else { |
| long time = getTimeAtX(e.x); |
| if (Math.abs(e.x - xBegin) < SNAP_WIDTH && Math.abs(time - selectionBegin) <= Math.abs(time - selectionEnd)) { |
| fDragBeginMarker = true; |
| fDragX = xEnd; |
| fDragX0 = e.x; |
| fDragTime0 = selectionEnd; |
| } else if (Math.abs(e.x - xEnd) < SNAP_WIDTH && Math.abs(time - selectionEnd) <= Math.abs(time - selectionBegin)) { |
| fDragX0 = xBegin; |
| fDragTime0 = selectionBegin; |
| } |
| } |
| fTime0bak = fTimeProvider.getTime0(); |
| fTime1bak = fTimeProvider.getTime1(); |
| redraw(); |
| updateCursor(e.x, e.stateMask); |
| fTimeGraphScale.setDragRange(fDragX0, fDragX); |
| } |
| } else if (2 == e.button || (1 == e.button && (e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL)) { |
| long hitTime = getTimeAtX(e.x); |
| if (hitTime > 0) { |
| setCapture(true); |
| fDragState = DRAG_TRACE_ITEM; |
| fDragButton = e.button; |
| fDragX = e.x; |
| fDragX0 = fDragX; |
| fTime0bak = fTimeProvider.getTime0(); |
| fTime1bak = fTimeProvider.getTime1(); |
| updateCursor(e.x, e.stateMask); |
| } |
| } else if (3 == e.button) { |
| setCapture(true); |
| fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), getSize().x - RIGHT_MARGIN); |
| fDragX0 = fDragX; |
| fDragTime0 = getTimeAtX(fDragX0); |
| fDragState = DRAG_ZOOM; |
| fDragButton = e.button; |
| redraw(); |
| updateCursor(e.x, e.stateMask); |
| fTimeGraphScale.setDragRange(fDragX0, fDragX); |
| } |
| } |
| |
| @Override |
| public void mouseUp(MouseEvent e) { |
| if (fPendingMenuDetectEvent != null && e.button == 3) { |
| menuDetected(fPendingMenuDetectEvent); |
| } |
| if (DRAG_NONE != fDragState) { |
| setCapture(false); |
| if (e.button == fDragButton && DRAG_TRACE_ITEM == fDragState) { |
| if (fDragX != fDragX0) { |
| fTimeProvider.notifyStartFinishTime(); |
| } |
| fDragState = DRAG_NONE; |
| } else if (e.button == fDragButton && DRAG_SPLIT_LINE == fDragState) { |
| fDragState = DRAG_NONE; |
| redraw(); |
| } else if (e.button == fDragButton && DRAG_SELECTION == fDragState) { |
| if (fDragX == fDragX0) { // click without selecting anything |
| long time = getTimeAtX(e.x); |
| fTimeProvider.setSelectedTimeNotify(time, false); |
| } else { |
| long time0 = fDragBeginMarker ? getTimeAtX(fDragX0) : fDragTime0; |
| long time1 = fDragBeginMarker ? fDragTime0 : getTimeAtX(fDragX); |
| fTimeProvider.setSelectionRangeNotify(time0, time1); |
| } |
| fDragState = DRAG_NONE; |
| redraw(); |
| fTimeGraphScale.setDragRange(-1, -1); |
| } else if (e.button == fDragButton && DRAG_ZOOM == fDragState) { |
| int nameWidth = fTimeProvider.getNameSpace(); |
| if (Math.max(fDragX, fDragX0) > nameWidth && fDragX != fDragX0) { |
| long time0 = getTimeAtX(fDragX0); |
| long time1 = getTimeAtX(fDragX); |
| if (time0 < time1) { |
| fTimeProvider.setStartFinishTimeNotify(time0, time1); |
| } else { |
| fTimeProvider.setStartFinishTimeNotify(time1, time0); |
| } |
| } else { |
| redraw(); |
| } |
| fDragState = DRAG_NONE; |
| fTimeGraphScale.setDragRange(-1, -1); |
| } |
| } |
| updateCursor(e.x, e.stateMask); |
| updateStatusLine(e.x); |
| } |
| |
| @Override |
| public void mouseEnter(MouseEvent e) { |
| } |
| |
| @Override |
| public void mouseExit(MouseEvent e) { |
| if (fMouseOverSplitLine) { |
| fMouseOverSplitLine = false; |
| redraw(); |
| } |
| updateStatusLine(STATUS_WITHOUT_CURSOR_TIME); |
| } |
| |
| @Override |
| public void mouseHover(MouseEvent e) { |
| } |
| |
| @Override |
| public void mouseScrolled(MouseEvent e) { |
| if (fDragState != DRAG_NONE) { |
| return; |
| } |
| boolean zoomScroll = false; |
| boolean horizontalScroll = 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 > getSize().x) { |
| // over the vertical scroll bar |
| zoomScroll = false; |
| } else if (e.y < 0) { |
| // over the time scale |
| zoomScroll = true; |
| } else if (e.y >= getSize().y) { |
| // over the horizontal scroll bar |
| if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) { |
| zoomScroll = true; |
| } else { |
| horizontalScroll = true; |
| } |
| } else { |
| if (e.x < fTimeProvider.getNameSpace()) { |
| // over the name space |
| zoomScroll = false; |
| } else { |
| // over the state area |
| if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) { |
| // over the state area, CTRL pressed |
| zoomScroll = true; |
| } else { |
| // over the state area, CTRL not pressed |
| zoomScroll = false; |
| } |
| } |
| } |
| } |
| if (zoomScroll && fTimeProvider.getTime0() != fTimeProvider.getTime1()) { |
| if (e.count > 0) { |
| zoom(true); |
| } else if (e.count < 0) { |
| zoom(false); |
| } |
| } else if (horizontalScroll) { |
| horizontalScroll(e.count > 0); |
| } else { |
| setTopIndex(getTopIndex() - e.count); |
| } |
| } |
| |
| @Override |
| public void handleEvent(Event event) { |
| if (event.type == SWT.MouseWheel) { |
| // prevent horizontal scrolling when the mouse wheel is used to |
| // scroll vertically or zoom |
| event.doit = false; |
| } |
| } |
| |
| @Override |
| public int getBorderWidth() { |
| return fBorderWidth; |
| } |
| |
| /** |
| * Set the border width |
| * |
| * @param borderWidth |
| * The width |
| */ |
| public void setBorderWidth(int borderWidth) { |
| this.fBorderWidth = borderWidth; |
| } |
| |
| /** |
| * @return The current height of the header row |
| */ |
| public int getHeaderHeight() { |
| return fHeaderHeight; |
| } |
| |
| /** |
| * Set the height of the header row |
| * |
| * @param headerHeight |
| * The height |
| */ |
| public void setHeaderHeight(int headerHeight) { |
| this.fHeaderHeight = headerHeight; |
| } |
| |
| /** |
| * @return The default height of regular item rows |
| */ |
| public int getItemHeight() { |
| return fGlobalItemHeight; |
| } |
| |
| /** |
| * Set the default height of regular item rows. |
| * |
| * @param rowHeight |
| * The height |
| */ |
| public void setItemHeight(int rowHeight) { |
| this.fGlobalItemHeight = rowHeight; |
| } |
| |
| /** |
| * Set the height of a specific item. Overrides the default item height. |
| * |
| * @param entry |
| * A time graph entry |
| * @param rowHeight |
| * The height |
| * @return true if the height is successfully stored, false otherwise |
| */ |
| public boolean setItemHeight(ITimeGraphEntry entry, int rowHeight) { |
| Item item = fItemData.findItem(entry); |
| if (item != null) { |
| item.fItemHeight = rowHeight; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Set the minimum item width |
| * |
| * @param width The minimum width |
| */ |
| public void setMinimumItemWidth(int width) { |
| this.fMinimumItemWidth = width; |
| } |
| |
| /** |
| * @return The minimum item width |
| */ |
| public int getMinimumItemWidth() { |
| return fMinimumItemWidth; |
| } |
| |
| /** |
| * Set whether all time events with a duration shorter than one pixel should |
| * be blended in. If false, only the first such time event will be drawn and |
| * the subsequent time events in the same pixel will be discarded. The |
| * default value is false. |
| * |
| * @param blend |
| * true if sub-pixel events should be blended, false otherwise. |
| * @since 2.0 |
| */ |
| public void setBlendSubPixelEvents(boolean blend) { |
| fBlendSubPixelEvents = blend; |
| } |
| |
| /** |
| * @return The entries that are currently filtered out |
| */ |
| public List<ITimeGraphEntry> getFilteredOut() { |
| return fItemData.getFilteredOut(); |
| } |
| |
| @Override |
| public void addSelectionChangedListener(ISelectionChangedListener listener) { |
| if (listener != null && !fSelectionChangedListeners.contains(listener)) { |
| fSelectionChangedListeners.add(listener); |
| } |
| } |
| |
| @Override |
| public void removeSelectionChangedListener(ISelectionChangedListener listener) { |
| if (listener != null) { |
| fSelectionChangedListeners.remove(listener); |
| } |
| } |
| |
| @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); |
| } |
| } |
| |
| } |
| |
| /** |
| * @param filter The filter object to be attached to the view |
| */ |
| public void addFilter(ViewerFilter filter) { |
| if (!fFilters.contains(filter)) { |
| fFilters.add(filter); |
| } |
| } |
| |
| /** |
| * @param filter The filter object to be attached to the view |
| */ |
| public void removeFilter(ViewerFilter filter) { |
| fFilters.remove(filter); |
| } |
| |
| @Override |
| public void colorSettingsChanged(StateItem[] stateItems) { |
| /* Destroy previous colors from the resource manager */ |
| if (fEventColorMap != null) { |
| for (Color color : fEventColorMap) { |
| fResourceManager.destroyColor(color.getRGB()); |
| } |
| } |
| 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[] { }; |
| } |
| redraw(); |
| } |
| |
| private class ItemData { |
| private final Map<ITimeGraphEntry, Item> fItemMap = new LinkedHashMap<>(); |
| private Item[] fExpandedItems = new Item[0]; |
| private Item[] fItems = new Item[0]; |
| private ITimeGraphEntry fRootEntries[] = new ITimeGraphEntry[0]; |
| private List<ILinkEvent> fLinks = new ArrayList<>(); |
| private boolean fEntryFilter[] = new boolean[0]; |
| private final ArrayList<ITimeGraphEntry> fFilteredOut = new ArrayList<>(); |
| |
| public ItemData() { |
| } |
| |
| public Item findItem(ITimeGraphEntry entry) { |
| return fItemMap.get(entry); |
| } |
| |
| public int findItemIndex(ITimeGraphEntry entry) { |
| Item item = fItemMap.get(entry); |
| if (item == null) { |
| return -1; |
| } |
| return item.fExpandedIndex; |
| } |
| |
| public void refreshData() { |
| fItemMap.clear(); |
| fFilteredOut.clear(); |
| ITimeGraphEntry selection = getSelectedTrace(); |
| for (int i = 0; i < fRootEntries.length; i++) { |
| ITimeGraphEntry entry = fRootEntries[i]; |
| refreshData(fItemMap, null, 0, entry); |
| } |
| fItems = fItemMap.values().toArray(new Item[0]); |
| updateExpandedItems(); |
| if (selection != null) { |
| for (Item item : fExpandedItems) { |
| if (item.fEntry == selection) { |
| item.fSelected = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| private void refreshData(Map<ITimeGraphEntry, Item> itemMap, Item parent, int level, ITimeGraphEntry entry) { |
| Item item = new Item(entry, entry.getName(), level); |
| if (parent != null) { |
| parent.fChildren.add(item); |
| } |
| if (fGlobalItemHeight == CUSTOM_ITEM_HEIGHT) { |
| item.fItemHeight = fTimeGraphProvider.getItemHeight(entry); |
| } else { |
| item.fItemHeight = fGlobalItemHeight; |
| } |
| itemMap.put(entry, item); |
| if (entry.hasChildren()) { |
| item.fExpanded = fAutoExpandLevel == ALL_LEVELS || level < fAutoExpandLevel; |
| item.fHasChildren = true; |
| for (ITimeGraphEntry child : entry.getChildren()) { |
| refreshData(itemMap, item, level + 1, child); |
| } |
| } |
| } |
| |
| public void updateExpandedItems() { |
| for (Item item : fItems) { |
| item.fExpandedIndex = -1; |
| } |
| List<Item> expandedItemList = new ArrayList<>(); |
| for (int i = 0; i < fRootEntries.length; i++) { |
| ITimeGraphEntry entry = fRootEntries[i]; |
| Item item = findItem(entry); |
| refreshExpanded(expandedItemList, item); |
| } |
| fExpandedItems = expandedItemList.toArray(new Item[0]); |
| fTopIndex = Math.min(fTopIndex, Math.max(0, fExpandedItems.length - 1)); |
| } |
| |
| private void refreshExpanded(List<Item> expandedItemList, Item item) { |
| // Check for filters |
| boolean display = true; |
| for (ViewerFilter filter : fFilters) { |
| if (!filter.select(null, item.fEntry.getParent(), item.fEntry)) { |
| display = false; |
| break; |
| } |
| } |
| if (display) { |
| item.fExpandedIndex = expandedItemList.size(); |
| expandedItemList.add(item); |
| if (item.fHasChildren && item.fExpanded) { |
| for (Item child : item.fChildren) { |
| refreshExpanded(expandedItemList, child); |
| } |
| } |
| } |
| } |
| |
| public void refreshData(ITimeGraphEntry[] entries) { |
| if (entries == null) { |
| fEntryFilter = null; |
| fRootEntries = null; |
| } else { |
| if (entries.length == 0) { |
| fEntryFilter = null; |
| } else if (fEntryFilter == null || entries.length != fEntryFilter.length) { |
| fEntryFilter = new boolean[entries.length]; |
| java.util.Arrays.fill(fEntryFilter, true); |
| } |
| fRootEntries = Arrays.copyOf(entries, entries.length); |
| } |
| |
| refreshData(); |
| } |
| |
| public void refreshArrows(List<ILinkEvent> events) { |
| /* If links are null, reset the list */ |
| if (events != null) { |
| fLinks = events; |
| } else { |
| fLinks = new ArrayList<>(); |
| } |
| } |
| |
| public ITimeGraphEntry[] getEntries() { |
| return fRootEntries; |
| } |
| |
| public boolean[] getEntryFilter() { |
| return fEntryFilter; |
| } |
| |
| public List<ITimeGraphEntry> getFilteredOut() { |
| return fFilteredOut; |
| } |
| } |
| |
| private class Item { |
| private boolean fExpanded; |
| private int fExpandedIndex; |
| private boolean fSelected; |
| private boolean fHasChildren; |
| private int fItemHeight; |
| private final int fLevel; |
| private final List<Item> fChildren; |
| private final String fName; |
| private final ITimeGraphEntry fEntry; |
| |
| public Item(ITimeGraphEntry entry, String name, int level) { |
| this.fEntry = entry; |
| this.fName = name; |
| this.fLevel = level; |
| this.fChildren = new ArrayList<>(); |
| } |
| |
| @Override |
| public String toString() { |
| return fName; |
| } |
| } |
| |
| @Override |
| public void menuDetected(MenuDetectEvent e) { |
| if (null == fTimeProvider) { |
| return; |
| } |
| if (e.detail == SWT.MENU_MOUSE) { |
| if (fPendingMenuDetectEvent == null) { |
| /* Feature in Linux. The MenuDetectEvent is received before mouseDown. |
| * Store the event and trigger it later just before handling mouseUp. |
| * This allows for the method to detect if mouse is used to drag zoom. |
| */ |
| fPendingMenuDetectEvent = e; |
| return; |
| } |
| fPendingMenuDetectEvent = null; |
| if (fDragState != DRAG_ZOOM || fDragX != fDragX0) { |
| return; |
| } |
| } else { |
| if (fDragState != DRAG_NONE) { |
| return; |
| } |
| } |
| Point p = toControl(e.x, e.y); |
| int idx = getItemIndexAtY(p.y); |
| if (idx >= 0 && idx < fItemData.fExpandedItems.length) { |
| Item item = fItemData.fExpandedItems[idx]; |
| ITimeGraphEntry entry = item.fEntry; |
| if (entry.hasTimeEvents()) { |
| ITimeEvent event = Utils.findEvent(entry, getTimeAtX(p.x), 2); |
| if (event != null) { |
| e.data = event; |
| fireMenuEventOnTimeEvent(e); |
| return; |
| } |
| } |
| e.data = entry; |
| fireMenuEventOnTimeGraphEntry(e); |
| } |
| } |
| |
| /** |
| * Perform the alignment operation. |
| * |
| * @param offset |
| * the alignment offset |
| * |
| * @see ITmfTimeAligned |
| * |
| * @since 1.0 |
| */ |
| public void performAlign(int offset) { |
| fTimeProvider.setNameSpace(offset); |
| } |
| |
| /** |
| * Return the time alignment information |
| * |
| * @return the time alignment information |
| * |
| * @see ITmfTimeAligned |
| * |
| * @since 1.0 |
| */ |
| public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() { |
| return new TmfTimeViewAlignmentInfo(getShell(), toDisplay(0, 0), fTimeProvider.getNameSpace()); |
| } |
| } |
| |
| |