/***************************************************************************** * Copyright (c) 2007, 2013 Intel Corporation, 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 * Vitaly A. Provodin, Intel - Initial API and implementation * Alvaro Sanchez-Leon - Updated for TMF * Patrick Tasse - Refactoring *****************************************************************************/ package fr.inria.linuxtools.tmf.ui.widgets.timegraph.widgets; import java.util.Iterator; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; 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.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import fr.inria.linuxtools.internal.tmf.ui.Messages; import fr.inria.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; import fr.inria.linuxtools.tmf.ui.widgets.timegraph.model.ILinkEvent; import fr.inria.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent; import fr.inria.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; import fr.inria.linuxtools.tmf.ui.widgets.timegraph.model.NullTimeEvent; import fr.inria.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat; import fr.inria.soctrace.lib.model.utils.TimestampFormat; /** * Handler for the tool tips in the generic time graph view. * * @version 1.0 * @author Alvaro Sanchez-Leon * @author Patrick Tasse */ public class TimeGraphTooltipHandler { private static final int OFFSET = 16; // @Framesoc private final TimestampFormat contextFormatter = new TimestampFormat(); private final TimestampFormat simpleFormatter = new TimestampFormat(); private Shell fTipShell; private Composite fTipComposite; private final ITimeDataProvider fTimeDataProvider; private ITimeGraphPresentationProvider fTimeGraphProvider = null; /** * Standard constructor * * @param graphProv * The presentation provider * @param timeProv * The time provider * * @since 2.0 */ public TimeGraphTooltipHandler(ITimeGraphPresentationProvider graphProv, ITimeDataProvider timeProv) { this.fTimeGraphProvider = graphProv; this.fTimeDataProvider = timeProv; } private void createTooltipShell(Shell parent) { final Display display = parent.getDisplay(); if (fTipShell != null && !fTipShell.isDisposed()) { fTipShell.dispose(); } fTipShell = new Shell(parent, SWT.ON_TOP | SWT.TOOL); GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 2; gridLayout.marginWidth = 2; gridLayout.marginHeight = 2; fTipShell.setLayout(gridLayout); fTipShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); fTipComposite = new Composite(fTipShell, SWT.NONE); fTipComposite.setLayout(new GridLayout(3, false)); setupControl(fTipComposite); } /** * Callback for the mouse-over tooltip * * @param control * The control object to use */ public void activateHoverHelp(final Control control) { control.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { if (fTipShell != null && !fTipShell.isDisposed()) { fTipShell.dispose(); } } }); control.addMouseMoveListener(new MouseMoveListener() { @Override public void mouseMove(MouseEvent e) { if (fTipShell != null && !fTipShell.isDisposed()) { fTipShell.dispose(); } } }); control.addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseExit(MouseEvent e) { if (fTipShell != null && !fTipShell.isDisposed()) { Point pt = control.toDisplay(e.x, e.y); if (!fTipShell.getBounds().contains(pt)) { fTipShell.dispose(); } } } private void addItem(String name, String value) { Label nameLabel = new Label(fTipComposite, SWT.NO_FOCUS); nameLabel.setText(name); setupControl(nameLabel); Label separator = new Label(fTipComposite, SWT.NO_FOCUS | SWT.SEPARATOR | SWT.VERTICAL); GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false); gd.heightHint = nameLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; separator.setLayoutData(gd); setupControl(separator); Label valueLabel = new Label(fTipComposite, SWT.NO_FOCUS); valueLabel.setText(value); setupControl(valueLabel); } /* * @Framesoc Using the Framesoc Timestamp formatter instead of * Utils.formatTime() */ private void fillValues(Point pt, TimeGraphControl timeGraphControl, ITimeGraphEntry entry) { if (entry == null) { return; } if (entry.hasTimeEvents()) { long currPixelTime = timeGraphControl.getTimeAtX(pt.x); long nextPixelTime = timeGraphControl.getTimeAtX(pt.x + 1); if (nextPixelTime == currPixelTime) { nextPixelTime++; } ITimeEvent currEvent = Utils.findEvent(entry, currPixelTime, 0); ITimeEvent nextEvent = Utils.findEvent(entry, currPixelTime, 1); // if there is no current event at the start of the current // pixel range, // or if the current event starts before the current pixel // range, // use the next event as long as it starts within the // current pixel range if ((currEvent == null || currEvent.getTime() < currPixelTime) && (nextEvent != null && nextEvent.getTime() < nextPixelTime)) { currEvent = nextEvent; currPixelTime = nextEvent.getTime(); } // state name String stateTypeName = fTimeGraphProvider.getStateTypeName(entry); String entryName = entry.getName(); if (stateTypeName == null) { stateTypeName = fTimeGraphProvider.getStateTypeName(); } if (!entryName.isEmpty()) { addItem(stateTypeName, entry.getName()); } if (currEvent == null || currEvent instanceof NullTimeEvent) { return; } // state String state = fTimeGraphProvider.getEventName(currEvent); if (state != null) { addItem(Messages.TmfTimeTipHandler_TRACE_STATE, state); } // This block receives a list of <String, String> values to // be added to the tip table Map<String, String> eventAddOns = fTimeGraphProvider.getEventHoverToolTipInfo(currEvent, currPixelTime); if (eventAddOns != null) { for (Iterator<String> iter = eventAddOns.keySet().iterator(); iter.hasNext();) { String message = iter.next(); addItem(message, eventAddOns.get(message)); } } if (fTimeGraphProvider.displayTimesInTooltip()) { long eventStartTime = -1; long eventDuration = -1; long eventEndTime = -1; eventStartTime = currEvent.getTime(); eventDuration = currEvent.getDuration(); if (eventDuration < 0 && nextEvent != null) { eventEndTime = nextEvent.getTime(); eventDuration = eventEndTime - eventStartTime; } else { eventEndTime = eventStartTime + eventDuration; } // Resolution res = Resolution.NANOSEC; TimeFormat tf = fTimeDataProvider.getTimeFormat(); if (tf == TimeFormat.CALENDAR) { addItem(Messages.TmfTimeTipHandler_TRACE_DATE, eventStartTime > -1 ? Utils.formatDate(eventStartTime) : "?"); //$NON-NLS-1$ } if (eventDuration > 0) { contextFormatter.setTimeUnit(fTimeGraphProvider.getTimeUnit()); contextFormatter.setContext(eventStartTime, eventEndTime); addItem(Messages.TmfTimeTipHandler_TRACE_START_TIME, eventStartTime > -1 ? contextFormatter.format(eventStartTime) : "?"); //$NON-NLS-1$ addItem(Messages.TmfTimeTipHandler_TRACE_STOP_TIME, eventEndTime > -1 ? contextFormatter.format(eventEndTime) : "?"); //$NON-NLS-1$ } else { simpleFormatter.setTimeUnit(fTimeGraphProvider.getTimeUnit()); addItem(Messages.TmfTimeTipHandler_TRACE_EVENT_TIME, eventStartTime > -1 ? simpleFormatter.format(eventStartTime) : "?"); //$NON-NLS-1$ } if (eventDuration > 0) { // Duration in relative format in any case if (tf == TimeFormat.CALENDAR) { tf = TimeFormat.RELATIVE; } simpleFormatter.setTimeUnit(fTimeGraphProvider.getTimeUnit()); addItem(Messages.TmfTimeTipHandler_DURATION, eventDuration > -1 ? simpleFormatter.format(eventDuration) : "?"); //$NON-NLS-1$ } } } } /* * @Framesoc Using the Framesoc Timestamp formatter instead of * Utils.formatTime() */ private void fillValues(ILinkEvent linkEvent) { addItem(Messages.TmfTimeTipHandler_LINK_SOURCE, linkEvent.getEntry().getName()); addItem(Messages.TmfTimeTipHandler_LINK_TARGET, linkEvent.getDestinationEntry().getName()); // This block receives a list of <String, String> values to be // added to the tip table Map<String, String> eventAddOns = fTimeGraphProvider.getEventHoverToolTipInfo(linkEvent); if (eventAddOns != null) { for (Iterator<String> iter = eventAddOns.keySet().iterator(); iter.hasNext();) { String message = iter.next(); addItem(message, eventAddOns.get(message)); } } if (fTimeGraphProvider.displayTimesInTooltip()) { long sourceTime = linkEvent.getTime(); long duration = linkEvent.getDuration(); long targetTime = sourceTime + duration; // Resolution res = Resolution.NANOSEC; TimeFormat tf = fTimeDataProvider.getTimeFormat(); if (tf == TimeFormat.CALENDAR) { addItem(Messages.TmfTimeTipHandler_TRACE_DATE, Utils.formatDate(sourceTime)); } if (duration > 0) { simpleFormatter.setTimeUnit(fTimeGraphProvider.getTimeUnit()); contextFormatter.setTimeUnit(fTimeGraphProvider.getTimeUnit()); contextFormatter.setContext(sourceTime, targetTime); addItem(Messages.TmfTimeTipHandler_LINK_SOURCE_TIME, contextFormatter.format(sourceTime)); addItem(Messages.TmfTimeTipHandler_LINK_TARGET_TIME, contextFormatter.format(targetTime)); // Duration in relative format in any case if (tf == TimeFormat.CALENDAR) { tf = TimeFormat.RELATIVE; } addItem(Messages.TmfTimeTipHandler_DURATION, simpleFormatter.format(duration)); } else { simpleFormatter.setTimeUnit(fTimeGraphProvider.getTimeUnit()); addItem(Messages.TmfTimeTipHandler_LINK_TIME, simpleFormatter.format(sourceTime)); } } } @Override public void mouseHover(MouseEvent event) { if ((event.stateMask & SWT.BUTTON_MASK) != 0) { return; } Point pt = new Point(event.x, event.y); TimeGraphControl timeGraphControl = (TimeGraphControl) event.widget; createTooltipShell(timeGraphControl.getShell()); for (Control child : fTipComposite.getChildren()) { child.dispose(); } if ((event.stateMask & SWT.MODIFIER_MASK) != SWT.SHIFT) { ILinkEvent linkEvent = timeGraphControl.getArrow(pt); if (linkEvent != null) { fillValues(linkEvent); } } if (fTipComposite.getChildren().length == 0) { ITimeGraphEntry entry = timeGraphControl.getEntry(pt); fillValues(pt, timeGraphControl, entry); } if (fTipComposite.getChildren().length == 0) { return; } fTipShell.pack(); Point tipPosition = control.toDisplay(pt); fTipShell.pack(); setHoverLocation(fTipShell, tipPosition); fTipShell.setVisible(true); } }); } private static void setHoverLocation(Shell shell, Point position) { Rectangle displayBounds = shell.getDisplay().getBounds(); Rectangle shellBounds = shell.getBounds(); if (position.x + shellBounds.width + OFFSET > displayBounds.width && position.x - shellBounds.width - OFFSET >= 0) { shellBounds.x = position.x - shellBounds.width - OFFSET; } else { shellBounds.x = Math.max(Math.min(position.x + OFFSET, displayBounds.width - shellBounds.width), 0); } if (position.y + shellBounds.height + OFFSET > displayBounds.height && position.y - shellBounds.height - OFFSET >= 0) { shellBounds.y = position.y - shellBounds.height - OFFSET; } else { shellBounds.y = Math.max(Math.min(position.y + OFFSET, displayBounds.height - shellBounds.height), 0); } shell.setBounds(shellBounds); } private void setupControl(Control control) { control.setForeground(fTipShell.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND)); control.setBackground(fTipShell.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); control.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { fTipShell.dispose(); } }); control.addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseExit(MouseEvent e) { fTipShell.dispose(); } }); control.addMouseMoveListener(new MouseMoveListener() { @Override public void mouseMove(MouseEvent e) { fTipShell.dispose(); } }); } }