/******************************************************************************* * 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.views; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.FontDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; import org.eclipse.wb.swt.ResourceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fr.inria.soctrace.framesoc.core.FramesocStartup; import fr.inria.soctrace.framesoc.core.bus.FramesocBus; import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopic; import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopicList; import fr.inria.soctrace.framesoc.core.bus.FramesocBusVariable; import fr.inria.soctrace.framesoc.core.bus.IFramesocBusListener; import fr.inria.soctrace.framesoc.ui.Activator; import fr.inria.soctrace.framesoc.ui.dialogs.TraceFilterDialog; import fr.inria.soctrace.framesoc.ui.handlers.HandlerUtils; import fr.inria.soctrace.framesoc.ui.loaders.TraceLoader; import fr.inria.soctrace.framesoc.ui.model.FolderNode; import fr.inria.soctrace.framesoc.ui.model.TimeInterval; import fr.inria.soctrace.framesoc.ui.model.TraceIntervalDescriptor; import fr.inria.soctrace.framesoc.ui.model.TraceNode; import fr.inria.soctrace.framesoc.ui.perspective.FramesocPartManager; import fr.inria.soctrace.framesoc.ui.perspective.FramesocViews; import fr.inria.soctrace.framesoc.ui.providers.TreeContentProvider; import fr.inria.soctrace.framesoc.ui.providers.TreeLabelProvider; import fr.inria.soctrace.framesoc.ui.utils.TraceSelection; import fr.inria.soctrace.framesoc.ui.utils.UpdateAssistant; import fr.inria.soctrace.lib.model.Trace; import fr.inria.soctrace.lib.model.utils.SoCTraceException; /** * View displaying all the traces. * * @author "Generoso Pagano <generoso.pagano@inria.fr>" */ public class TraceTreeView extends ViewPart implements IFramesocBusListener { /** * The ID of the view as specified by the extension. */ public static final String ID = FramesocViews.TRACE_TREE_VIEW_ID; /** * Commands to remove at multi selection */ private static final String COMMAND_FRAMESOC_PARTS = "fr.inria.soctrace.framesoc.ui.popup.parts"; //$NON-NLS-1$ private static final String COMMAND_COPY_DB_NAME = Activator.PLUGIN_ID + ".commands.CopyToClipboard"; //$NON-NLS-1$ /** * Logger */ private final static Logger logger = LoggerFactory.getLogger(TraceTreeView.class); private TreeViewer viewer; private TraceLoader tracesLoader; private FramesocBusTopicList topics; private Set<Trace> checked; private LocalResourceManager resourceManager = new LocalResourceManager( JFaceResources.getResources()); private Font boldFont; private Font normalFont; /** * The listener we register with the selection service */ private ISelectionListener listener = new ISelectionListener() { public void selectionChanged(IWorkbenchPart sourcepart, ISelection selection) { if (sourcepart.equals(TraceTreeView.this) && selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) selection; if (!TraceSelection.isSelectionValid(ss)) return; setSelectionBusVariables(ss); } } }; /** * Set the passed selection on the bus * * @param ss * valid structured selection containing traces */ private void setSelectionBusVariables(IStructuredSelection ss) { FramesocBus bus = FramesocBus.getInstance(); bus.setVariable(FramesocBusVariable.TRACE_VIEW_SELECTED_TRACE, TraceSelection.getTraceFromSelection(ss)); bus.setVariable(FramesocBusVariable.TRACE_VIEW_CURRENT_TRACE_SELECTION, ss); logger.debug("# of selected: {}", ss.size()); logger.debug("Selected Trace: {}", bus.getVariable(FramesocBusVariable.TRACE_VIEW_SELECTED_TRACE)); logger.debug("Current Trace selection: {}", bus.getVariable(FramesocBusVariable.TRACE_VIEW_CURRENT_TRACE_SELECTION)); } /** * Constructor. Register to Framesoc Notification Bus. */ public TraceTreeView() { topics = new FramesocBusTopicList(this); topics.addTopic(FramesocBusTopic.TOPIC_UI_FOCUSED_TRACE); topics.addTopic(FramesocBusTopic.TOPIC_UI_SYNCH_TRACES_NEEDED); topics.addTopic(FramesocBusTopic.TOPIC_UI_SYSTEM_INITIALIZED); topics.addTopic(FramesocBusTopic.TOPIC_UI_REFRESH_TRACES_NEEDED); topics.addTopic(FramesocBusTopic.TOPIC_UI_HIGHLIGHT_TRACES); topics.registerAll(); } @Override public void createPartControl(Composite parent) { if(!FramesocStartup.Started) UpdateAssistant.checkDB(); checked = new HashSet<>(); tracesLoader = new TraceLoader(); viewer = new TreeViewer(parent, SWT.MULTI); viewer.setContentProvider(new TreeContentProvider()); viewer.setLabelProvider(new TreeLabelProvider()); viewer.setInput(tracesLoader.loadFromDB()); // default comparator viewer.setComparator(new ViewerComparator()); // double click viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { IStructuredSelection thisSelection = (IStructuredSelection) event.getSelection(); if (TraceSelection.isFolderNode(thisSelection)) { FolderNode folder = (FolderNode) thisSelection.getFirstElement(); boolean expanded = viewer.getExpandedState(folder); viewer.setExpandedState(folder, !expanded); return; } if (!TraceSelection.isSelectionValid(thisSelection) || !FramesocPartManager.getInstance().isFramesocPartExisting( FramesocViews.GANTT_CHART_VIEW_ID)) { return; } setSelectionBusVariables(thisSelection); TraceNode selectedNode = (TraceNode) thisSelection.getFirstElement(); TraceIntervalDescriptor des = new TraceIntervalDescriptor(); des.setTrace(selectedNode.getTrace()); des.setTimeInterval(TimeInterval.NOT_SPECIFIED); des.setGroup(FramesocPartManager.NEW_GROUP); logger.debug(des.toString()); FramesocBus.getInstance().send( FramesocBusTopic.TOPIC_UI_GANTT_DISPLAY_TIME_INTERVAL, des); } }); // key listener viewer.getControl().addKeyListener(new KeyAdapter() { @Override public void keyReleased(final KeyEvent event) { if (event.keyCode == SWT.DEL) { HandlerUtils.launchCommand(getSite(), "fr.inria.soctrace.framesoc.ui.commands.deleteDB"); } } }); // Context menu MenuManager menuManager = new MenuManager(); Menu menu = menuManager.createContextMenu(viewer.getTree()); viewer.getTree().setMenu(menu); getSite().registerContextMenu(menuManager, viewer); menuManager.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { // show the menu only on leaf nodes IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); if (!TraceSelection.isSelectionValid(selection)) { manager.removeAll(); return; } if (selection.size() > 1) { manager.remove(COMMAND_FRAMESOC_PARTS); manager.remove(COMMAND_COPY_DB_NAME); } setSelectionBusVariables(selection); } }); // build the toolbar buildToolbar(parent); // register the selection listener getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(listener); // provide our selection for other viewers getSite().setSelectionProvider(viewer); // expand all viewer.expandAll(); viewer.refresh(); // resources boldFont = resourceManager.createFont(FontDescriptor.createFrom(viewer.getTree().getFont()) .setStyle(SWT.BOLD)); normalFont = viewer.getTree().getFont(); } @Override public void setFocus() { viewer.getControl().setFocus(); } @Override public void dispose() { // unregister listeners topics.unregisterAll(); getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(listener); super.dispose(); } /** * Load the traces from the System DB. */ private void loadTracesFromDB() { viewer.setInput(tracesLoader.loadFromDB()); viewer.refresh(); applyChecked(true); } /** * Synchronize Traces view with the System DB. */ private void synchTracesWithDB() { FolderNode[] roots = tracesLoader.getRoots(); Object[] path = viewer.getExpandedElements(); try { roots = tracesLoader.synchWithDB(); } catch (SoCTraceException e) { MessageDialog.openError(getSite().getShell(), "Exception", e.getMessage()); } viewer.setInput(roots); viewer.setExpandedElements(path); applyChecked(true); } /** * Synchronize viewer input with the data model. */ private void synchTracesWithModel() { Object[] path = viewer.getExpandedElements(); viewer.setInput(tracesLoader.synchWithModel()); viewer.setExpandedElements(path); applyChecked(true); } // utilities private void buildToolbar(final Composite parent) { // create refresh the action Action refreshAction = new Action() { public void run() { synchTracesWithDB(); } }; refreshAction.setText("Refresh traces"); refreshAction.setToolTipText("Refresh traces"); refreshAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages() .getImageDescriptor(ISharedImages.IMG_ELCL_SYNCED)); // create the expand all action Action expandAction = new Action() { public void run() { viewer.expandAll(); } }; expandAction.setText("Expand all"); expandAction.setToolTipText("Expand all"); expandAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor( Activator.PLUGIN_ID, "icons/expandall.gif")); // create the collapse all action Action collapseAction = new Action() { public void run() { viewer.collapseAll(); } }; collapseAction.setText("Collapse all"); collapseAction.setToolTipText("Collapse all"); collapseAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor( Activator.PLUGIN_ID, "icons/collapseall.gif")); // create the search action Action searchAction = new Action() { public void run() { TraceFilterDialog filterDialog = new TraceFilterDialog(getSite().getShell()); List<Trace> traces = tracesLoader.getTraces(); filterDialog.init(traces, checked); if (filterDialog.open() == Dialog.OK) { checked = filterDialog.getChecked(); applyChecked(true); } } }; searchAction.setText("Search traces"); searchAction.setToolTipText("Search traces"); searchAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor( Activator.PLUGIN_ID, "icons/search.gif")); // create the clear action Action cleanAction = new Action() { public void run() { checked = new HashSet<>(); applyChecked(false); } }; cleanAction.setText("Clean highlight"); cleanAction.setToolTipText("Clean highlight"); cleanAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor( Activator.PLUGIN_ID, "icons/clear_co.gif")); // add to toolbar IActionBars actionBars = getViewSite().getActionBars(); IToolBarManager toolBar = actionBars.getToolBarManager(); toolBar.add(expandAction); toolBar.add(collapseAction); toolBar.add(new Separator()); toolBar.add(searchAction); toolBar.add(cleanAction); toolBar.add(new Separator()); toolBar.add(refreshAction); } private void applyChecked(boolean check) { if (check && checked.size() > 0) { viewer.expandAll(); } TreeItem[] items = viewer.getTree().getItems(); for (TreeItem item : items) { check(item, check); } } private void check(TreeItem item, boolean check) { if (item.getData() instanceof FolderNode) { for (TreeItem s : item.getItems()) { check(s, check); } } else { TraceNode node = (TraceNode) item.getData(); if (node == null) return; if (check && checked.contains(node.getTrace())) { item.setFont(boldFont); } else { item.setFont(normalFont); } } } @Override public void handle(FramesocBusTopic topic, Object data) { if (topic.equals(FramesocBusTopic.TOPIC_UI_FOCUSED_TRACE) && data != null) { TraceNode node = tracesLoader.getTraceNode((Trace) data); if (node == null) return; IStructuredSelection sel = new StructuredSelection(node); viewer.setSelection(sel, true); FramesocBus.getInstance().setVariable(FramesocBusVariable.TRACE_VIEW_SELECTED_TRACE, (Trace) data); FramesocBus.getInstance().setVariable( FramesocBusVariable.TRACE_VIEW_CURRENT_TRACE_SELECTION, sel); logger.debug( "Selected trace: {}", FramesocBus.getInstance() .getVariable(FramesocBusVariable.TRACE_VIEW_SELECTED_TRACE).toString()); logger.debug( "Current trace selection: {}", FramesocBus.getInstance() .getVariable(FramesocBusVariable.TRACE_VIEW_CURRENT_TRACE_SELECTION) .toString()); logger.debug("# of selected: {}", sel.size()); } else if (topic.equals(FramesocBusTopic.TOPIC_UI_SYNCH_TRACES_NEEDED) && data != null) { if ((Boolean) data) { synchTracesWithDB(); } } else if (topic.equals(FramesocBusTopic.TOPIC_UI_SYSTEM_INITIALIZED)) { loadTracesFromDB(); } else if (topic.equals(FramesocBusTopic.TOPIC_UI_REFRESH_TRACES_NEEDED)) { synchTracesWithModel(); } else if (topic.equals(FramesocBusTopic.TOPIC_UI_HIGHLIGHT_TRACES)) { @SuppressWarnings("unchecked") List<Trace> traces = (List<Trace>) data; for (Trace t : traces) { if (checked.contains(t)) { checked.remove(t); } else { checked.add(t); } } applyChecked(true); } } }