/*******************************************************************************
* Copyright (c) 2012-2015 INRIA.
* 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:
* Generoso Pagano - initial API and implementation
******************************************************************************/
package fr.inria.soctrace.framesoc.ui.gantt.view;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.wb.swt.ResourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.inria.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
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.TimeGraphEntry;
import fr.inria.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils;
import fr.inria.soctrace.framesoc.core.bus.FramesocBus;
import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopic;
import fr.inria.soctrace.framesoc.ui.colors.FramesocColor;
import fr.inria.soctrace.framesoc.ui.colors.FramesocColorManager;
import fr.inria.soctrace.framesoc.ui.gantt.Activator;
import fr.inria.soctrace.framesoc.ui.gantt.GanttContributionManager;
import fr.inria.soctrace.framesoc.ui.gantt.loaders.CpuEventDrawer;
import fr.inria.soctrace.framesoc.ui.gantt.loaders.GanttEntry;
import fr.inria.soctrace.framesoc.ui.gantt.model.IEventDrawer;
import fr.inria.soctrace.framesoc.ui.gantt.model.IEventLoader;
import fr.inria.soctrace.framesoc.ui.gantt.model.ReducedEvent;
import fr.inria.soctrace.framesoc.ui.gantt.provider.GanttPresentationProvider;
import fr.inria.soctrace.framesoc.ui.gantt.snapshot.GanttSnapshotDialog;
import fr.inria.soctrace.framesoc.ui.model.CategoryNode;
import fr.inria.soctrace.framesoc.ui.model.ColorsChangeDescriptor;
import fr.inria.soctrace.framesoc.ui.model.EventProducerNode;
import fr.inria.soctrace.framesoc.ui.model.EventTableDescriptor;
import fr.inria.soctrace.framesoc.ui.model.EventTypeNode;
import fr.inria.soctrace.framesoc.ui.model.HistogramTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.ITreeNode;
import fr.inria.soctrace.framesoc.ui.model.LoaderQueue;
import fr.inria.soctrace.framesoc.ui.model.PieTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.SynchronizeTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.TableTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.TimeInterval;
import fr.inria.soctrace.framesoc.ui.model.TraceConfigurationDescriptor;
import fr.inria.soctrace.framesoc.ui.model.TraceIntervalDescriptor;
import fr.inria.soctrace.framesoc.ui.perspective.FramesocPartManager;
import fr.inria.soctrace.framesoc.ui.perspective.FramesocViews;
import fr.inria.soctrace.framesoc.ui.providers.EventProducerTreeLabelProvider;
import fr.inria.soctrace.framesoc.ui.utils.AlphanumComparator;
import fr.inria.soctrace.lib.model.EventProducer;
import fr.inria.soctrace.lib.model.EventType;
import fr.inria.soctrace.lib.model.Trace;
import fr.inria.soctrace.lib.model.utils.ModelConstants.EventCategory;
import fr.inria.soctrace.lib.model.utils.ModelConstants.ModelEntity;
import fr.inria.soctrace.lib.utils.Configuration;
import fr.inria.soctrace.lib.utils.DeltaManager;
import fr.inria.soctrace.lib.utils.Configuration.SoCTraceProperty;
/**
* Gantt View using the Lttng Time Graph Viewer.
*
* <pre>
* TODO: add support for filtering (making invisible) specific links,
* as done for states and punctual events.
* Constraints:
* - invisible links must not be considered when filtering because of resolution
* - if at least one link type is visible, the hide arrows action must be unchecked
* - if all links are invisible the hide arrows action must be checked
* - if the hide arrows action is checked, all link types must be invisible
* - if the hide arrows action is unchecked, all the link types must be visible
* </pre>
*
* @author "Generoso Pagano <generoso.pagano@inria.fr>"
*/
public class GanttView extends AbstractGanttView {
/**
* Logger
*/
private static final Logger logger = LoggerFactory
.getLogger(GanttView.class);
/**
* Event producer column
*/
public static final String PRODUCER = "Event Producer";
/**
* The ID of the view as specified by the extension.
*/
public static final String ID = FramesocViews.GANTT_CHART_VIEW_ID;
/**
* Time graph presentation provider
*/
private GanttPresentationProvider fPresentationProvider;
/**
* The event loader job
*/
private Job loaderJob;
/**
* The event drawer thread
*/
private Thread drawerThread;
/**
* Largest requested and loaded interval for the current shown trace
*/
private TimeInterval loadedInterval = new TimeInterval(Long.MAX_VALUE,
Long.MIN_VALUE);
/**
* Links
*/
private List<ILinkEvent> links;
/**
* Event Producer filter dialog action
*/
private IAction producerFilterAction;
/**
* Event Type filter dialog action
*/
private IAction typeFilterAction;
/**
* Hide arrows action
*/
private IAction hideArrowsAction;
/**
* Percentage of displayed arrows
*/
private double arrowPercentage;
/**
* Roots of type hierarchy
*/
private CategoryNode[] typeHierarchy;
/**
* Tree nodes corresponding to checked nodes.
*/
private List<Object> visibleTypeNodes;
/**
* Number of event type nodes
*/
private int allTypeNodes;
/**
* Should the Gantt focus on a particular event ?
*/
private boolean focusOnEvent = false;
/**
* Information about the event the Gantt should be focused on
*/
private TraceConfigurationDescriptor focusEventDescriptor = null;
/**
* Flag specifying if we use the CPU drawer?
*/
private boolean forceCpuDrawer = false;
/**
* Flag indicating saying to check if we need to set up the filters (event
* producers and event types)
*/
private boolean setFilters = false;
private ArrayList<Integer> mergedIDs;
public boolean isForceCpuDrawer() {
return forceCpuDrawer;
}
public void setForceCpuDrawer(boolean forceCpuDrawer) {
this.forceCpuDrawer = forceCpuDrawer;
}
/**
* Constructor
*/
public GanttView() {
super(ID, new GanttPresentationProvider());
setTreeColumns(new String[] { PRODUCER });
setTreeLabelProvider(new TimeGraphTreeLabelProvider());
setFilterColumns(new String[] { PRODUCER });
setFilterLabelProvider(new TimeGraphTreeLabelProvider());
setEntryComparator(new GanttViewEntryComparator());
fPresentationProvider = (GanttPresentationProvider) getPresentationProvider();
topics.addTopic(FramesocBusTopic.TOPIC_UI_TRACES_SYNCHRONIZED);
topics.addTopic(FramesocBusTopic.TOPIC_UI_REFRESH_TRACES_NEEDED);
topics.addTopic(FramesocBusTopic.TOPIC_UI_COLORS_CHANGED);
topics.addTopic(FramesocBusTopic.TOPIC_UI_SYSTEM_INITIALIZED);
topics.registerAll();
}
private static class GanttViewEntryComparator implements
Comparator<ITimeGraphEntry> {
@Override
public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
if ((o1 instanceof TimeGraphEntry)
&& (o2 instanceof TimeGraphEntry)) {
int ret = AlphanumComparator
.compare(o1.getName(), o2.getName());
return ret;
}
return 0;
}
}
@Override
public void setFocus() {
super.setFocus();
}
@Override
public void dispose() {
if (loaderJob != null)
loaderJob.cancel();
if (drawerThread != null)
drawerThread.interrupt();
super.dispose();
}
@Override
public void partHandle(FramesocBusTopic topic, Object data) {
if (topic.equals(FramesocBusTopic.TOPIC_UI_COLORS_CHANGED)) {
if (currentShownTrace == null)
return;
ColorsChangeDescriptor des = (ColorsChangeDescriptor) data;
logger.debug("Colors changed: {}", des);
if (des.getEntity().equals(ModelEntity.EVENT_TYPE)) {
fPresentationProvider.updateColors();
refresh();
}
}
}
@Override
public String getId() {
return ID;
}
/**
* Show the trace.
*
* @param data
* if null, all the trace must be loaded. Otherwise data is a
* TraceIntervalDescriptor.
*/
@Override
public void showTrace(Trace trace, Object data) {
if (data == null) {
showWindow(trace, trace.getMinTimestamp(), trace.getMaxTimestamp());
} else {
TraceIntervalDescriptor des = (TraceIntervalDescriptor) data;
if (des.getTimeInterval().equals(TimeInterval.NOT_SPECIFIED)) {
// double click
showWindow(des.getTrace(), des.getTrace().getMinTimestamp(),
des.getTrace().getMaxTimestamp());
} else {
long start = des.getStartTimestamp();
long end = des.getEndTimestamp();
// Are we in the case where we should focus on an event
if (data instanceof TraceConfigurationDescriptor) {
focusEventDescriptor = (TraceConfigurationDescriptor) data;
// Check later if filters apply since we need loading to
// have taken place before performing the checks (check
// performed in DrawerThread)
setFilters = true;
if (focusEventDescriptor.isFocusOnEvent()) {
focusOnEvent = true;
// Change duration to load one percent of the trace
// around the event
long duration = Math
.max((des.getTrace().getMaxTimestamp() - des
.getTrace().getMinTimestamp()) / 100,
TraceConfigurationDescriptor.MIN_TIME_UNIT_SHOWING);
start = Math.max(des.getTrace().getMinTimestamp(),
des.getStartTimestamp() - duration / 2);
end = Math.min(des.getTrace().getMaxTimestamp(),
des.getEndTimestamp() + duration / 2);
// If it is within the already loaded interval
if ((start >= loadedInterval.startTimestamp)
&& (end <= loadedInterval.endTimestamp)) {
// Don't reload
start = loadedInterval.startTimestamp;
end = loadedInterval.endTimestamp;
}
}
}
showWindow(des.getTrace(), start, end);
}
}
}
private void showWindow(Trace trace, long start, long end) {
if (trace == null) {
return;
}
// create the queue
LoaderQueue<ReducedEvent> queue = new LoaderQueue<>();
// create the event loader
IEventLoader loader = GanttContributionManager.getEventLoader(trace
.getType().getId());
loader.setTrace(trace);
loader.setQueue(queue);
// compute the actual time interval to load
TimeInterval interval = new TimeInterval(start, end);
// check for unchanged interval
if (checkReuse(trace, interval)) {
loader.release();
if (setFilters) {
setFilters = false;
checkFilters();
}
refresh(interval);
// Do we need to focus on a particular event ?
if (focusOnEvent) {
focusOnEvent = false;
focusViewOn(focusEventDescriptor);
}
return;
}
// create the event drawer
IEventDrawer drawer;
if (forceCpuDrawer) {
drawer = new CpuEventDrawer();
} else {
drawer = GanttContributionManager.getEventDrawer(trace.getType()
.getId());
}
drawer.setProducers(loader.getProducers());
// launch the job loading the queue
launchLoaderJob(loader, interval);
// update the viewer
launchDrawerThread(drawer, interval, trace, queue);
}
private void reloadWindow(Trace trace, long start, long end) {
if (trace == null) {
return;
}
// create the queue
LoaderQueue<ReducedEvent> queue = new LoaderQueue<>();
// create the event loader
IEventLoader loader = GanttContributionManager.getEventLoader(trace
.getType().getId());
loader.setTrace(trace);
loader.setQueue(queue);
// compute the actual time interval to load
TimeInterval interval = new TimeInterval(start, end);
// create the event drawer
IEventDrawer drawer = null;
if (forceCpuDrawer) {
drawer = new CpuEventDrawer();
} else {
drawer = GanttContributionManager.getEventDrawer(trace.getType()
.getId());
}
drawer.setProducers(loader.getProducers());
// launch the job loading the queue
launchLoaderJob(loader, interval);
// update the viewer
launchDrawerThread(drawer, interval, trace, queue);
}
private boolean checkReuse(Trace trace, TimeInterval interval) {
if (trace.equals(currentShownTrace)
&& (interval.startTimestamp >= loadedInterval.startTimestamp)
&& (interval.endTimestamp <= loadedInterval.endTimestamp)) {
return true;
}
loadedInterval = interval;
setStartTime(Long.MAX_VALUE);
setEndTime(Long.MIN_VALUE);
refresh();
return false;
}
private void launchLoaderJob(final IEventLoader loader,
final TimeInterval interval) {
loaderJob = new Job("Loading Gantt Chart...") {
@Override
protected IStatus run(IProgressMonitor monitor) {
DeltaManager all = new DeltaManager();
all.start();
Collection<EventType> types = loader.getTypes().values();
fPresentationProvider.setTypes(types);
typeHierarchy = getTypeHierarchy(types);
visibleTypeNodes = listAllInputs(new ArrayList<CategoryNode>(Arrays.asList(typeHierarchy)));
allTypeNodes = visibleTypeNodes.size();
loader.loadWindow(interval.startTimestamp,
interval.endTimestamp, monitor);
loader.release();
logger.debug(all.endMessage("Loader Job: loaded everything"));
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
return Status.OK_STATUS;
}
};
loaderJob.setUser(false);
loaderJob.schedule();
}
private void launchDrawerThread(final IEventDrawer drawer,
final TimeInterval requestedInterval, final Trace trace,
final LoaderQueue<ReducedEvent> queue) {
enableActions(true);
drawerThread = new Thread() {
@Override
public void run() {
DeltaManager all = new DeltaManager();
all.start();
DeltaManager dm = new DeltaManager();
setEntryComparator(new GanttViewEntryComparator());
if (getEntryList() != null)
getEntryList().clear();
boolean closeIfCancelled = true;
currentShownTrace = trace;
long min = trace.getMinTimestamp();
long max = trace.getMaxTimestamp();
setMinTime(min);
setMaxTime(max);
setStartTime(Long.MAX_VALUE);
setEndTime(Long.MIN_VALUE);
long waited = 0;
TimeInterval partial = null;
while (!queue.done()) {
try {
dm.start();
List<ReducedEvent> events = queue.pop();
dm.end();
waited += dm.getDelta();
if (events.isEmpty())
continue;
// prepare the viewer model
partial = drawer.draw(events);
// update start / end timestamp
long newStart = Math
.max(requestedInterval.startTimestamp, Math
.min(partial.startTimestamp,
getStartTime()));
long newEnd = Math.min(requestedInterval.endTimestamp,
Math.max(partial.endTimestamp, getEndTime()));
// update start time
setStartTime(newStart);
// update end time
setEndTime(newEnd);
// update entry list
addToEntryList(drawer.getNewRootEntries());
// copy the list for concurrent access
links = sortLinks(new ArrayList<>(drawer.getLinks()));
boolean needRefresh = drawer.needRefresh()
|| (newEnd > getEndTime())
|| newStart < getStartTime();
if (needRefresh) {
refresh();
// Do not resize the Gantt after the first display
setfUserChangedTimeRange(true);
} else {
redraw();
}
closeIfCancelled = false;
} catch (InterruptedException e) {
logger.debug("Interrupted while taking the queue head");
}
}
// Merge entries only at the end to avoid a bug where some
// producers are not shown
mergeEntries();
refresh();
TimeInterval queueInterval = queue.getTimeInterval();
if (queue.isComplete()) {
// the whole requested interval has been loaded
setStartTime(Math.max(requestedInterval.startTimestamp,
queueInterval.startTimestamp));
setEndTime(Math.min(requestedInterval.endTimestamp,
queueInterval.endTimestamp));
// Update loadedInterval values
loadedInterval.startTimestamp = getStartTime();
loadedInterval.endTimestamp = getEndTime();
} else {
// something has not been loaded
if (partial != null && queueInterval != null) {
// something has been loaded
setStartTime(Math.max(requestedInterval.startTimestamp,
queueInterval.startTimestamp));
setEndTime(Math.min(partial.endTimestamp, Math.min(
requestedInterval.endTimestamp,
queueInterval.endTimestamp)));
loadedInterval.startTimestamp = getStartTime();
loadedInterval.endTimestamp = getEndTime();
}
handleCancel(closeIfCancelled);
}
// Apply filter from synchronization with other views
if (setFilters) {
setFilters = false;
checkFilters();
}
// Should we focus on an event ?
if (focusOnEvent) {
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
focusOnEvent = false;
focusViewOn(focusEventDescriptor);
}
});
}
// refresh one last time
refresh();
// release drawer
drawer.release();
logger.debug(all
.endMessage("Drawer Thread: visualizing everything"));
logger.debug("Waited {} ms", waited);
logger.debug("LoadedInterval {}", loadedInterval.toString());
logger.debug("start: {}", getStartTime());
logger.debug("end: {}", getEndTime());
}
};
drawerThread.start();
}
protected void mergeEntries() {
GanttEntry root = null;
for (TimeGraphEntry entry : fEntryList) {
if (entry.getParent() == null) {
root = (GanttEntry) entry;
break;
}
}
List<TimeGraphEntry> mergedEntries = new ArrayList<TimeGraphEntry>();
mergedIDs = new ArrayList<Integer>();
if (root != null && root.getChildren().size() == 1) {
TimeGraphEntry child = root.getChildren().get(0);
while (child.getChildren().size() == 1) {
if (!((GanttEntry) child).isProducingEvent()) {
root.setName(root.getName() + " - " + child.getName());
root.getChildren().clear();
root.addChild(child.getChildren().get(0));
mergedEntries.add(child);
mergedIDs.add(((GanttEntry) child).getEventProducerID());
child = child.getChildren().get(0);
} else {
break;
}
}
root.setName(root.getName() + " - " + child.getName());
root.getChildren().clear();
for (TimeGraphEntry children : child.getChildren()) {
root.addChild(children);
}
mergedEntries.add(child);
mergedIDs.add(((GanttEntry) child).getEventProducerID());
for (TimeGraphEntry children : mergedEntries.get(
mergedEntries.size() - 1).getChildren()) {
children.setParent(root);
}
}
fEntryList.removeAll(mergedEntries);
}
private List<ILinkEvent> sortLinks(List<ILinkEvent> arrayList) {
Collections.sort(arrayList, new Comparator<ILinkEvent>() {
@Override
public int compare(ILinkEvent e1, ILinkEvent e2) {
if (e1.getTime() > e2.getTime())
return 1;
if (e1.getTime() == e2.getTime())
return 0;
return -1;
}
});
return arrayList;
}
@Override
protected List<ILinkEvent> getLinkList(long startTime, long endTime,
long resolution, IProgressMonitor monitor) {
if (links == null)
return null;
List<ILinkEvent> actualLinks = new ArrayList<>();
long actualStart = startTime;
int intersecting = 0; // links intersecting the time interval
// iterate over the *sorted* list of events
for (ILinkEvent link : links) {
if (monitor.isCanceled()) {
return null;
}
// move the time cursor
actualStart = Math.max(link.getTime(), actualStart);
if (link.getTime() > endTime
|| (link.getTime() + link.getDuration()) < startTime) {
// link not intersecting the interval
continue;
}
intersecting++;
if (link.getDuration() > resolution) {
// link whose duration is visible
actualLinks.add(link);
} else {
// for links whose duration is smaller than or equal to the
// resolution pick only one
if (link.getTime() >= actualStart) {
actualLinks.add(link);
actualStart += resolution;
}
}
}
updateLinksText(actualLinks.size(), intersecting);
return actualLinks;
}
private void handleCancel(final boolean closeIfCancelled) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (closeIfCancelled) {
FramesocPartManager.getInstance().disposeFramesocPart(
GanttView.this);
GanttView.this.hideView();
}
}
});
}
@Override
protected void fillLocalToolBar(IToolBarManager manager) {
// Filters
producerFilterAction = getTimeGraphCombo().getShowFilterAction();
getTimeGraphCombo().getFilterDialog().setLabelProvider(
new EventProducerTreeLabelProvider());
getTimeGraphCombo().getFilterDialog().setComparator(
new ViewerComparator(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return AlphanumComparator.compare(o1, o2);
}
}));
manager.add(producerFilterAction);
typeFilterAction = createShowTypeFilterAction();
manager.add(typeFilterAction);
hideArrowsAction = createHideArrowsAction();
manager.add(hideArrowsAction);
manager.add(new Separator());
// snapshot
manager.add(createSnapshotAction());
manager.add(new Separator());
// zoom
manager.add(getTimeGraphViewer().getResetScaleAction());
manager.add(getTimeGraphViewer().getZoomInAction());
manager.add(getTimeGraphViewer().getZoomOutAction());
manager.add(new Separator());
// navigation
manager.add(getTimeGraphViewer().getPreviousEventAction());
manager.add(getTimeGraphViewer().getNextEventAction());
manager.add(getPreviousResourceAction());
manager.add(getNextResourceAction());
manager.add(new Separator());
// others
manager.add(getTimeGraphViewer().getShowLegendAction());
manager.add(createCpuDrawerAction());
manager.add(new Separator());
// Framesoc
TableTraceIntervalAction.add(manager, createTableAction());
PieTraceIntervalAction.add(manager, createPieAction());
HistogramTraceIntervalAction.add(manager, createHistogramAction());
SynchronizeTraceIntervalAction.add(manager, createSynchronizeAction());
// TEST ACTION
// manager.add(new Action("Test Action", IAction.AS_PUSH_BUTTON) {
// @Override
// public void run() {
//
// }
// });
}
protected TraceIntervalDescriptor getIntervalDescriptor() {
if (currentShownTrace == null)
return null;
TraceConfigurationDescriptor des = new TraceConfigurationDescriptor();
des.setTrace(currentShownTrace);
des.setStartTimestamp(getStartTime());
des.setEndTimestamp(getEndTime());
// Set event producers and event types filter
Set<ITimeGraphEntry> entries = getTimeGraphViewer()
.getTimeGraphControl().getEntries();
des.setEventProducers(convertToEventProducerNode(entries));
des.setEventTypes(visibleTypeNodes);
return des;
}
private IAction createShowTypeFilterAction() {
IAction action = new Action("", IAction.AS_CHECK_BOX) {
@Override
public void run() {
showTypeFilterAction();
}
};
action.setImageDescriptor(ResourceManager.getPluginImageDescriptor(
Activator.PLUGIN_ID, "icons/filter_types.gif"));
action.setToolTipText("Show Event Type Filter");
return action;
}
private IAction createHideArrowsAction() {
DialogSettings settings = (DialogSettings) Activator.getDefault()
.getDialogSettings();
final IAction defaultAction = getTimeGraphCombo().getTimeGraphViewer()
.getHideArrowsAction(settings);
IAction action = new Action("", IAction.AS_CHECK_BOX) {
@Override
public void run() {
boolean hideArrows = hideArrowsAction.isChecked();
defaultAction.setChecked(hideArrows);
defaultAction.run();
refresh();
if (hideArrows) {
setArrowPercentage(0.0);
} else {
setArrowPercentage(arrowPercentage);
}
}
};
action.setImageDescriptor(defaultAction.getImageDescriptor());
action.setToolTipText(defaultAction.getToolTipText());
action.setChecked(defaultAction.isChecked());
return action;
}
private IAction createCpuDrawerAction() {
IAction action = new Action("", IAction.AS_CHECK_BOX) {
@Override
public void run() {
setForceCpuDrawer(isChecked());
reloadWindow(currentShownTrace, getStartTime(), getEndTime());
}
};
action.setImageDescriptor(ResourceManager.getPluginImageDescriptor(
Activator.PLUGIN_ID, "icons/cpu_node.png"));
action.setToolTipText("Use CPU Drawer");
return action;
}
private void updateLinksText(final double shown, final double intersecting) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (intersecting == 0) {
arrowPercentage = 100;
} else {
arrowPercentage = (shown / intersecting) * 100;
}
if (hideArrowsAction != null && !hideArrowsAction.isChecked()) {
setArrowPercentage(arrowPercentage);
}
}
});
}
/**
* Get the event type hierarchy
*
* @param types
* collection of event types
* @return an array containing the roots of the type hierarchy
*/
public CategoryNode[] getTypeHierarchy(Collection<EventType> types) {
Map<Integer, CategoryNode> categories = new HashMap<>();
for (EventType et : types) {
EventTypeNode etn = new EventTypeNode(et);
if (et.getCategory() == EventCategory.LINK
|| et.getCategory() == EventCategory.VARIABLE) {
// skip links and variables
continue;
}
if (!categories.containsKey(et.getCategory())) {
categories.put(et.getCategory(),
new CategoryNode(et.getCategory()));
}
categories.get(et.getCategory()).addChild(etn);
}
return categories.values().toArray(
new CategoryNode[categories.values().size()]);
}
/**
* Explores the list of top-level inputs and returns all the inputs
*
* @param inputs
* The top-level inputs
* @return All the inputs
*/
private List<Object> listAllInputs(List<? extends ITreeNode> inputs) {
ArrayList<Object> items = new ArrayList<>();
for (ITreeNode entry : inputs) {
items.add(entry);
if (entry.hasChildren()) {
items.addAll(listAllInputs(entry.getChildren()));
}
}
return items;
}
/**
* Callback for the show type filter action
*/
private void showTypeFilterAction() {
TimeGraphFilterDialog typeFilterDialog = getTypeFilterDialog();
if (typeHierarchy.length > 0) {
typeFilterDialog.setInput(typeHierarchy);
typeFilterDialog.setTitle("Event Type Filter");
typeFilterDialog.setMessage("Check the event types to show");
List<Object> allElements = listAllInputs(Arrays
.asList(typeHierarchy));
typeFilterDialog.setExpandedElements(allElements.toArray());
typeFilterDialog.setInitialElementSelections(visibleTypeNodes);
// Sort in alphabetical order
typeFilterDialog.setComparator(new ViewerComparator(
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return AlphanumComparator.compare(o1, o2);
}
}));
typeFilterDialog.create();
// reset checked status, managed manually
typeFilterAction.setChecked(!typeFilterAction.isChecked());
if (typeFilterDialog.open() != Window.OK) {
return;
}
// Process selected elements
if (typeFilterDialog.getResult() != null) {
visibleTypeNodes = new ArrayList<Object>(Arrays.asList(typeFilterDialog.getResult()));
checkTypeFilter(visibleTypeNodes.size() != allTypeNodes);
ArrayList<Object> filteredElements = new ArrayList<Object>(
allElements);
filteredElements.removeAll(visibleTypeNodes);
List<Integer> filteredTypes = new ArrayList<>(
filteredElements.size());
for (Object o : filteredElements) {
if (o instanceof EventTypeNode) {
EventTypeNode type = (EventTypeNode) o;
filteredTypes.add(type.getId());
}
}
fPresentationProvider.setFilteredTypes(filteredTypes);
} else {
fPresentationProvider.setFilteredTypes(Collections
.<Integer> emptyList());
}
refresh();
}
}
private void checkTypeFilter(boolean check) {
if (check) {
typeFilterAction.setChecked(true);
typeFilterAction
.setToolTipText("Show Event Type Filter (filter applied)");
} else {
typeFilterAction.setChecked(false);
typeFilterAction.setToolTipText("Show Event Type Filter");
}
}
/**
* Focus the Gantt chart on a specific event (time region), by selecting the
* corresponding row and centering the view on the event.
*
* @param des
* the parameters needed to focus on the event
*/
private void focusViewOn(TraceConfigurationDescriptor des) {
if (des.getEventProducer() == null) {
logger.debug("No event producer was provided in the descriptor");
return;
}
// Look for the entry corresponding to the event producer
ITimeGraphEntry entry = null;
for (ITimeGraphEntry tge : getTimeGraphViewer().getExpandedElements()) {
if (tge.getName().equals(des.getEventProducer().getName())) {
entry = tge;
break;
}
}
if (entry == null) {
logger.debug("The event producer ("
+ des.getEventProducer().toString()
+ ") was not found in the Gantt Chart");
return;
}
// Select the corresponding entry
getTimeGraphCombo().setSelection(entry);
// Zoom in on the desired part
getTimeGraphViewer().setStartFinishTimeNotify(des.getStartTimestamp(),
des.getEndTimestamp());
}
/**
* Initialize the snapshot action
*
* @return the action
*/
public IAction createSnapshotAction() {
SnapshotAction snapshotAction = new SnapshotAction("",
IAction.AS_PUSH_BUTTON);
snapshotAction.ganttView = this;
snapshotAction.setImageDescriptor(ResourceManager
.getPluginImageDescriptor(Activator.PLUGIN_ID,
"icons/snapshot.png"));
snapshotAction.setToolTipText("Take a snapshot");
return snapshotAction;
}
private class SnapshotAction extends Action {
public GanttView ganttView;
public SnapshotAction(String string, int asPushButton) {
super(string, asPushButton);
}
@Override
public void run() {
Rectangle bound = getTimeGraphCombo().getClientArea();
new GanttSnapshotDialog(getSite().getShell(), ganttView,
bound.width, bound.height).open();
}
}
/**
* Take a snaphsot of the Gantt Chart
*
* @param width
* width of the snapshot
* @param height
* height of the snapshot. Is ignored if full height is selected.
* @param fullHeight
* should we take the whole hierarchy
* @param includeTimeScale
* Should we take the time scale
* @param fileName
* name of the file where to save the snapshot
*/
public void takeSnapshot(int width, int height, boolean fullHeight,
boolean includeTimeScale, String fileName) {
int totalHeight = 0;
int hierarchyWidth = getTimeGraphCombo().getTreeViewer().getTree()
.getBounds().width;
int headerHeight = 0;
if (includeTimeScale)
headerHeight = getTimeGraphViewer().getHeaderHeight();
totalHeight = computeHeight(fullHeight, headerHeight, height);
Image image = new Image(Display.getCurrent(), width, totalHeight);
GC gc = new GC(image);
// Draw hierarchy and adjust the height
drawHierarchy(gc, fullHeight);
// Create a paint event to paint on the image graphic context
Event newEvent = new Event();
newEvent.display = Display.getCurrent();
newEvent.widget = getTimeGraphViewer().getTimeGraphControl();
PaintEvent paintEvent = new PaintEvent(newEvent);
paintEvent.gc = gc;
paintEvent.width = width;
paintEvent.height = totalHeight;
paintEvent.x = hierarchyWidth;
// Paint the graph
getTimeGraphViewer().getTimeGraphControl().takeSnapshot(paintEvent,
fullHeight);
// Paint the time scale
if (includeTimeScale) {
// Mask the extra hierarchy in the corner
gc.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_WIDGET_BACKGROUND));
gc.fillRectangle(0, totalHeight - headerHeight, hierarchyWidth,
headerHeight);
// Make the drawing starts at the bottom of the graph
paintEvent.y = totalHeight - headerHeight;
getTimeGraphViewer().getTimeGraphScale().takeSnapshot(paintEvent,
false);
}
// Save the file
ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { image.getImageData() };
loader.save(fileName, SWT.IMAGE_PNG);
// Clean stuff
image.dispose();
gc.dispose();
}
/**
* Draw the hierarchy
*
* @param gc
* the graphics context on which to draw
* @return the total height of the drawing
*/
private void drawHierarchy(GC gc, boolean fullHeight) {
ITimeGraphEntry[] graphEntries = getTimeGraphViewer()
.getExpandedElements();
int entryHeight = 0;
int currentHeight = 0;
int entryLevel = 0;
// Shift to apply when getting down one hierarchy level
int entryShifting = 20;
// Shift to center the name of the producer
int verticalShift = 5;
int width = getTimeGraphCombo().getTreeViewer().getTree().getBounds().width;
// Set colors
Color rectangleBgColor1 = Display.getCurrent().getSystemColor(
SWT.COLOR_WIDGET_BACKGROUND);
Color rectangleBgColor2 = Display.getCurrent().getSystemColor(
SWT.COLOR_LIST_BACKGROUND);
Color fgColor = Display.getCurrent().getSystemColor(
SWT.COLOR_LIST_FOREGROUND);
gc.setBackground(rectangleBgColor2);
gc.setForeground(fgColor);
int topIndex = getTimeGraphViewer().getTimeGraphControl().getTopIndex();
if (fullHeight)
// Draw all
topIndex = 0;
for (int i = topIndex; i < graphEntries.length; i++) {
entryHeight = getTimeGraphViewer().getTimeGraphControl()
.getItemHeight(graphEntries[i]);
entryLevel = getTimeGraphViewer().getTimeGraphControl()
.getItemLevel(graphEntries[i]) + 1;
// Alternate background colors for better visibility
if (i % 2 == 0) {
gc.setBackground(rectangleBgColor1);
} else {
gc.setBackground(rectangleBgColor2);
}
// Draw the entry
gc.fillRectangle(0, currentHeight, width, entryHeight);
gc.drawText(graphEntries[i].getName(), entryLevel * entryShifting,
currentHeight + verticalShift);
// Update total height
currentHeight += entryHeight;
}
}
/**
* Compute the height of the final image according to the given constraints
*
* @param fullHeight
* do we capture the whole height of the gantt
* @param timeScaleHeight
* do we capture the time scale
* @param askedHeight
* the height required by the user
* @return the computed height
*/
private int computeHeight(boolean fullHeight, int timeScaleHeight,
int askedHeight) {
ITimeGraphEntry[] graphEntries = getTimeGraphViewer()
.getExpandedElements();
int computedHeight = timeScaleHeight;
// Index of the top element currently displayed on the gantt
int topIndex = 0;
if (!fullHeight)
topIndex = getTimeGraphViewer().getTimeGraphControl().getTopIndex();
for (int i = topIndex; i < graphEntries.length; i++) {
// Update total height
computedHeight += getTimeGraphViewer().getTimeGraphControl()
.getItemHeight(graphEntries[i]);
}
// If the computed is larger than what was asked
if (!fullHeight && computedHeight > askedHeight)
computedHeight = askedHeight;
return computedHeight;
}
public String getSnapshotInfo() {
StringBuffer output = new StringBuffer();
output.append("\nDisplayed start timestamp: ");
output.append(getTimeGraphViewer().getTime0());
output.append("\nDisplayed end timestamp: ");
output.append(getTimeGraphViewer().getTime1());
output.append("\nSelection start timestamp: ");
output.append(getTimeGraphViewer().getSelectionBegin());
output.append("\nSelection end timestamp: ");
output.append(getTimeGraphViewer().getSelectionEnd());
output.append("\nLoaded start timestamp: ");
output.append(getTimeGraphViewer().getBeginTime());
output.append("\nLoaded end timestamp: ");
output.append(getTimeGraphViewer().getEndTime());
// Filtered types
String filteredTypes = "";
List<Integer> filteredType = fPresentationProvider.getFilteredTypes();
for (Object typeObject : listAllInputs(Arrays.asList(typeHierarchy))) {
if (typeObject instanceof EventTypeNode) {
if (filteredType.contains(((EventTypeNode) typeObject)
.getEventType().getId()))
filteredTypes = filteredTypes
+ ((EventTypeNode) typeObject).getEventType()
.getName() + ", ";
}
}
// If there was some filtered event type
if (!filteredTypes.isEmpty()) {
// Remove last separator
filteredTypes = filteredTypes.substring(0,
filteredTypes.length() - 2);
output.append("\nFiltered event types: ");
output.append(filteredTypes);
}
// Filtered event producers
String filteredEP = "";
for (Object objectEntry : getTimeGraphCombo().getFilteredEntries()) {
filteredEP = filteredEP + ((GanttEntry) objectEntry).getName()
+ ", ";
}
// If there was some filtered event producers
if (!filteredEP.isEmpty()) {
// Remove last separator
filteredEP = filteredEP.substring(0, filteredEP.length() - 2);
output.append("\nFiltered event producers: ");
output.append(filteredEP);
}
return output.toString();
}
@Override
public void createContextMenu() {
final Menu contextMenu = new Menu(getTimeGraphViewer()
.getTimeGraphControl());
getTimeGraphViewer().getTimeGraphControl().setContextMenu(contextMenu);
getTimeGraphViewer().getTimeGraphControl().setMenu(
getTimeGraphViewer().getTimeGraphControl().getContextMenu());
contextMenu.addMenuListener(new MenuAdapter() {
@Override
public void menuShown(MenuEvent menuEvent) {
// clean menu
MenuItem[] items = getTimeGraphViewer().getTimeGraphControl()
.getContextMenu().getItems();
for (int i = 0; i < items.length; i++) {
items[i].dispose();
}
final MouseEvent rightClickEvent = getTimeGraphViewer()
.getTimeGraphControl().getRightClickEvent();
if (rightClickEvent == null) {
return;
}
// Get the corresponding event
final ITimeEvent selectedEvent = Utils.findEvent(
getTimeGraphViewer().getTimeGraphControl()
.getExpandedElements()[getTimeGraphViewer()
.getTimeGraphControl().getItemIndexAtY(
rightClickEvent.y)],
getTimeGraphViewer().getTimeGraphControl().getTimeAtX(
rightClickEvent.x), 0);
// If there is no event at the given position
if (selectedEvent == null
|| fPresentationProvider
.getStateTableIndex(selectedEvent) < 0) {
return;
}
// Hide type
MenuItem hideType = new MenuItem(getTimeGraphViewer()
.getTimeGraphControl().getContextMenu(), SWT.NONE);
hideType.setText("Hide Type");
hideType.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Find the type ID
int typeIndex = fPresentationProvider
.getStateTableIndex(selectedEvent);
// Add it to the filtered types
List<Integer> filteredTypes = fPresentationProvider
.getFilteredTypes();
filteredTypes.add(typeIndex);
fPresentationProvider.setFilteredTypes(filteredTypes);
// Synchronize with filter action
for (Object o : visibleTypeNodes) {
if (o instanceof EventTypeNode) {
EventTypeNode type = (EventTypeNode) o;
if (type.getId() == typeIndex) {
visibleTypeNodes.remove(o);
typeFilterAction.setChecked(true);
break;
}
}
}
refresh();
}
});
// Change color
MenuItem changeColor = new MenuItem(contextMenu, SWT.NONE);
changeColor.setText("Change Color Type");
changeColor.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Get the current color
Color c = getTimeGraphViewer().getTimeGraphControl()
.getEventColorMap()[fPresentationProvider
.getStateTableIndex(selectedEvent)];
ColorDialog colorDialog = new ColorDialog(Display
.getDefault().getActiveShell());
// Set the default color of the color dialog to the
// current color
colorDialog.setRGB(new RGB(c.getRed(), c.getGreen(), c
.getBlue()));
RGB rgb = colorDialog.open();
// If a color was selected
if (rgb != null) {
Color newColor = new Color(Display.getDefault(),
rgb.red, rgb.green, rgb.blue);
FramesocColor fColor = new FramesocColor(rgb.red,
rgb.green, rgb.blue);
// Change the color
fPresentationProvider.getStateTable()[fPresentationProvider
.getStateTableIndex(selectedEvent)]
.setStateColor(rgb);
getTimeGraphViewer().getTimeGraphControl()
.getEventColorMap()[fPresentationProvider
.getStateTableIndex(selectedEvent)] = newColor;
// Save it in the general settings
FramesocColorManager
.getInstance()
.setEventTypeColor(
fPresentationProvider
.getEventName(selectedEvent),
fColor);
FramesocColorManager.getInstance()
.saveEventTypeColors();
refresh();
}
}
});
// Show in table (and highlight event)
MenuItem showInfo = new MenuItem(contextMenu, SWT.NONE);
showInfo.setText("Show in Event Table");
showInfo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
switchToEventTable(
selectedEvent,
fPresentationProvider
.getEventName(selectedEvent),
getTimeGraphViewer().getTimeGraphControl()
.getExpandedElements()[getTimeGraphViewer()
.getTimeGraphControl().getItemIndexAtY(
rightClickEvent.y)].getName());
}
});
}
});
}
/**
* Method that launch the event table view and focus on a specific event
*
* @param event
* the (Gantt) event to set the focus on
* @param typeName
* the type of the focused event
* @param eventProducerName
* the event producer name of the focused event
*/
private void switchToEventTable(ITimeEvent event, String typeName,
String eventProducerName) {
long startTimestamp = event.getTime();
long endTimestamp = event.getTime() + event.getDuration();
long duration = event.getDuration();
// Punctual event
if (duration == 0)
// Show only the minimum between MIN_TIME_UNIT_SHOWING time units
// around or one
// percent of the trace
duration = Math.min(
TraceConfigurationDescriptor.MIN_TIME_UNIT_SHOWING,
(currentShownTrace.getMaxTimestamp() - currentShownTrace
.getMinTimestamp()) / 100);
EventTableDescriptor des = new EventTableDescriptor();
des.setTrace(currentShownTrace);
des.setStartTimestamp(startTimestamp - duration / 2);
des.setEndTimestamp(endTimestamp + duration / 2);
des.setEventProducerName(eventProducerName);
des.setTypeName(typeName);
des.setEventStartTimeStamp(startTimestamp);
FramesocBus.getInstance().send(
FramesocBusTopic.TOPIC_UI_TABLE_DISPLAY_TIME_INTERVAL, des);
}
/**
* Apply the event producers and event types filters
*/
private void checkFilters() {
// Event producers
if (!focusEventDescriptor.getEventProducers().isEmpty()
&& Boolean.valueOf(Configuration.getInstance().get(
SoCTraceProperty.producer_filter_synhronization))) {
// Get the IDs of unfiltered EventProducers
List<Integer> ids = new ArrayList<Integer>();
for (Object o : focusEventDescriptor.getEventProducers()) {
if (o instanceof EventProducerNode) {
ids.add(((EventProducerNode) o).getEventProducer().getId());
}
}
List<Object> filteredEntries = new ArrayList<Object>();
for (ITimeGraphEntry entry : getTimeGraphViewer()
.getTimeGraphControl().getEntries()) {
if (entry instanceof GanttEntry) {
GanttEntry ganttEntry = (GanttEntry) entry;
if (!ids.contains(ganttEntry.getEventProducerID())) {
filteredEntries.add(ganttEntry);
}
}
}
getTimeGraphCombo().setFilteredEntries(filteredEntries);
}
// Event types
if (!focusEventDescriptor.getEventTypes().isEmpty()
&& Boolean.valueOf(Configuration.getInstance().get(
SoCTraceProperty.type_filter_synchronization))) {
if (typeHierarchy.length > 0) {
List<Object> allElements = listAllInputs(Arrays
.asList(typeHierarchy));
visibleTypeNodes = focusEventDescriptor.getEventTypes();
checkTypeFilter(!(focusEventDescriptor.getEventTypes().size() == allTypeNodes || focusEventDescriptor
.getEventTypes().isEmpty()));
ArrayList<Object> filteredElements = new ArrayList<Object>(
allElements);
filteredElements
.removeAll(focusEventDescriptor.getEventTypes());
List<Integer> filteredTypes = new ArrayList<>(
filteredElements.size());
for (Object o : filteredElements) {
if (o instanceof EventTypeNode) {
EventTypeNode type = (EventTypeNode) o;
filteredTypes.add(type.getId());
}
}
fPresentationProvider.setFilteredTypes(filteredTypes);
}
}
}
/**
* Convert the GanntEntry to EventProducerNode
*
* @param entries
* the entries of the Gantt
* @return the list of event producer nodes produced from the converted
* entries
*/
private List<Object> convertToEventProducerNode(Set<ITimeGraphEntry> entries) {
IEventLoader loader = GanttContributionManager
.getEventLoader(currentShownTrace.getType().getId());
loader.setTrace(currentShownTrace);
Map<Integer, EventProducer> eventProducers = loader.getProducers();
Map<Integer, EventProducerNode> eventProducersNode = new HashMap<Integer, EventProducerNode>();
List<Object> epNodes = new ArrayList<Object>();
EventProducerNode root = null;
// Create EP Nodes
for (ITimeGraphEntry entry : entries) {
if (entry instanceof GanttEntry) {
if (!getTimeGraphCombo().getFilteredEntries().contains(entry)) {
EventProducerNode epn = new EventProducerNode(
eventProducers.get(((GanttEntry) entry)
.getEventProducerID()));
eventProducersNode.put(epn.getId(), epn);
epNodes.add(epn);
}
}
}
// Handle merged entries
for (int epId : mergedIDs) {
EventProducerNode epn = new EventProducerNode(
eventProducers.get(epId));
eventProducersNode.put(epn.getId(), epn);
epNodes.add(epn);
}
// Rebuild hierarchy
for (ITimeGraphEntry entry : entries) {
if (entry instanceof GanttEntry) {
if (!getTimeGraphCombo().getFilteredEntries().contains(entry)) {
GanttEntry gEntry = (GanttEntry) entry;
EventProducerNode epn = eventProducersNode.get(gEntry
.getEventProducerID());
// Set parent
if (gEntry.getParent() != null) {
epn.setParent(eventProducersNode
.get(((GanttEntry) gEntry.getParent())
.getEventProducerID()));
} else {
root = epn;
}
// Set children
for (ITimeGraphEntry childEntry : gEntry.getChildren()) {
if (!getTimeGraphCombo().getFilteredEntries().contains(
childEntry)) {
epn.addChild(eventProducersNode
.get(((GanttEntry) childEntry)
.getEventProducerID()));
}
}
}
}
}
// Handle merged entries
if (!mergedIDs.isEmpty()) {
EventProducerNode rootChild = null;
EventProducerNode lastMerged = null;
for (int epId : mergedIDs) {
EventProducerNode epn = eventProducersNode.get(epId);
if (epn.getEventProducer().getParentId() >= 0) {
// Set parent
epn.setParent(eventProducersNode.get(epn.getEventProducer()
.getParentId()));
// Set children
eventProducersNode
.get(epn.getEventProducer().getParentId())
.addChild(epn);
// If parent is root
if (((EventProducerNode) epn.getParent())
.getEventProducer().getId() == root.getId()) {
rootChild = epn;
}
}
}
for (int epId : mergedIDs) {
EventProducerNode epn = eventProducersNode.get(epId);
if (!epn.hasChildren()) {
lastMerged = epn;
}
}
for (ITreeNode treenode : root.getChildren()) {
EventProducerNode epn = (EventProducerNode) treenode;
lastMerged.addChild(epn);
epn.setParent(lastMerged);
}
root.getChildren().clear();
root.addChild(rootChild);
}
return epNodes;
}
}