blob: 7b5eb647c771383fb67aecb720b715e2fe35415f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2017 Ericsson and others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Francois Chouinard - Initial API and implementation
* Bernd Hufmann - Added possibility to pin view
* Marc-Andre Laperle - Support for view alignment
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.views;
import java.text.MessageFormat;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener2;
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.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants;
import org.eclipse.tracecompass.internal.tmf.ui.Messages;
import org.eclipse.tracecompass.internal.tmf.ui.views.TimeAlignViewsAction;
import org.eclipse.tracecompass.internal.tmf.ui.views.TmfAlignmentSynchronizer;
import org.eclipse.tracecompass.tmf.core.component.ITmfComponent;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;
/**
* Basic abstract TMF view class implementation. <br>
* It registers any sub class to the signal manager for receiving and sending
* TMF signals. <br>
* Subclasses may optionally implement the {@link ITmfTimeAligned},
* {@link ITmfAllowMultiple} and {@link ITmfPinnable} interfaces to enable those
* features.
*
* @author Francois Chouinard
*/
public abstract class TmfView extends ViewPart implements ITmfComponent {
private static final TmfAlignmentSynchronizer TIME_ALIGNMENT_SYNCHRONIZER = TmfAlignmentSynchronizer.getInstance();
private final String fName;
/** This allows us to keep track of the view sizes */
private Composite fParentComposite;
private ControlAdapter fControlListener;
/**
* Action class for pinning of TmfView.
*/
protected PinTmfViewAction fPinAction;
private static TimeAlignViewsAction fAlignViewsAction;
/** The save action */
private IAction fSaveAction;
/**
* The separator used between the primary and secondary id of a view id.
*
* @since 3.2
*/
public static final String VIEW_ID_SEPARATOR = ":"; //$NON-NLS-1$
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructor. Creates a TMF view and registers to the signal manager.
*
* @param viewName
* A view name
*/
public TmfView(String viewName) {
super();
fName = viewName;
TmfSignalManager.register(this);
}
/**
* Disposes this view and de-registers itself from the signal manager
*/
@Override
public void dispose() {
TmfSignalManager.deregister(this);
/* Workaround for Bug 490400: Clear the action bars */
IActionBars bars = getViewSite().getActionBars();
bars.getToolBarManager().removeAll();
bars.getMenuManager().removeAll();
super.dispose();
}
// ------------------------------------------------------------------------
// ITmfComponent
// ------------------------------------------------------------------------
@Override
public String getName() {
return fName;
}
@Override
public void broadcast(TmfSignal signal) {
TmfSignalManager.dispatchSignal(signal);
}
@Override
public void broadcastAsync(TmfSignal signal) {
TmfSignalManager.dispatchSignalAsync(signal);
}
// ------------------------------------------------------------------------
// View pinning support
// ------------------------------------------------------------------------
/**
* Returns whether the view is pinned.
*
* @return if the view is pinned
*/
public boolean isPinned() {
return ((fPinAction != null) && (fPinAction.isPinned()));
}
/**
* Method adds a pin action to the TmfView. For example, this action can be used
* to ignore time synchronization signals from other TmfViews. <br>
*
* Uses {@link ITmfPinnable#setPinned(ITmfTrace)} to propagate the state of the
* action button.
*/
protected void contributePinActionToToolBar() {
if (fPinAction == null && this instanceof ITmfPinnable) {
fPinAction = new PinTmfViewAction((ITmfPinnable) this);
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
toolBarManager.add(new Separator(IWorkbenchActionConstants.PIN_GROUP));
toolBarManager.add(fPinAction);
}
}
@Override
public void createPartControl(final Composite parent) {
fParentComposite = parent;
IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
if (this instanceof ITmfAllowMultiple) {
contributeNewViewActionToLocalMenu(menuManager);
}
IAction saveAction = fSaveAction;
if (saveAction == null) {
saveAction = createSaveAction();
fSaveAction = saveAction;
}
if (saveAction != null) {
menuManager.add(new Separator());
menuManager.add(saveAction);
menuManager.add(new Separator());
}
if (this instanceof ITmfTimeAligned) {
contributeAlignViewsActionToLocalMenu(menuManager);
fControlListener = new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
/*
* When switching perspective, the view can be resized just before it is made
* visible. Queue the time alignment to ensure it occurs when the parent
* composite is visible.
*/
e.display.asyncExec(() -> {
TIME_ALIGNMENT_SYNCHRONIZER.handleViewResized(TmfView.this);
});
}
};
parent.addControlListener(fControlListener);
getSite().getPage().addPartListener(new IPartListener() {
@Override
public void partOpened(IWorkbenchPart part) {
// do nothing
}
@Override
public void partDeactivated(IWorkbenchPart part) {
// do nothing
}
@Override
public void partClosed(IWorkbenchPart part) {
if (part == TmfView.this && fControlListener != null && !fParentComposite.isDisposed()) {
fParentComposite.removeControlListener(fControlListener);
fControlListener = null;
getSite().getPage().removePartListener(this);
TIME_ALIGNMENT_SYNCHRONIZER.handleViewClosed(TmfView.this);
}
}
@Override
public void partBroughtToTop(IWorkbenchPart part) {
// do nothing
}
@Override
public void partActivated(IWorkbenchPart part) {
// do nothing
}
});
}
if (!menuManager.isEmpty()) {
menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
toolBarManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
/* Subclass tool bar contributions should be added to this group */
if (this instanceof ITmfPinnable) {
contributePinActionToToolBar();
}
}
/**
* Get the save action
*
* @return the save action
* @since 3.3
*/
protected @Nullable IAction createSaveAction() {
return null;
}
/**
* Add the "New view" action to the view menu. This action spawns a new view of
* the same type as the caller.
*/
private void contributeNewViewActionToLocalMenu(IMenuManager menuManager) {
if (!menuManager.isEmpty()) {
menuManager.add(new Separator());
}
if (this instanceof ITmfPinnable) {
MenuManager newViewMenu = new MenuManager(MessageFormat.format(Messages.TmfView_NewViewActionText, this.getTitle()));
newViewMenu.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEW_VIEW));
newViewMenu.setRemoveAllWhenShown(true);
newViewMenu.addMenuListener(new IMenuListener2() {
@Override
public void menuAboutToShow(IMenuManager mgr) {
Set<@NonNull ITmfTrace> openedTraces = TmfTraceManager.getInstance().getOpenedTraces();
ITmfTrace activeTrace = ((ITmfPinnable) TmfView.this).getTrace();
if (activeTrace != null) {
mgr.add(new NewTmfViewAction(TmfView.this, activeTrace, true));
mgr.add(new Separator());
}
for (ITmfTrace trace : openedTraces) {
mgr.add(new NewTmfViewAction(TmfView.this, trace));
}
mgr.add(new Separator());
mgr.add(new NewTmfViewAction(TmfView.this, null));
}
@Override
public void menuAboutToHide(IMenuManager manager) {
/* Clear menu to release action references to trace instances */
manager.removeAll();
}
});
menuManager.add(newViewMenu);
} else {
NewTmfViewAction action = new NewTmfViewAction(TmfView.this);
menuManager.add(action);
}
}
private static void contributeAlignViewsActionToLocalMenu(IMenuManager menuManager) {
if (fAlignViewsAction == null) {
fAlignViewsAction = new TimeAlignViewsAction();
}
if (!menuManager.isEmpty()) {
menuManager.add(new Separator());
}
menuManager.add(fAlignViewsAction);
}
/**
* Returns the parent control of the view
*
* @return the parent control
*
* @since 1.0
*/
public Composite getParentComposite() {
return fParentComposite;
}
/**
* Return the Eclipse view ID in the format 'Primary ID':'Secondary ID' or
* simply 'Primary ID' if secondary ID is null
*
* @return This view's view ID
* @since 2.2
*/
protected @NonNull String getViewId() {
IViewSite viewSite = getViewSite();
String secondaryId = viewSite.getSecondaryId();
if (secondaryId == null) {
return String.valueOf(viewSite.getId());
}
return viewSite.getId() + VIEW_ID_SEPARATOR + secondaryId;
}
}