| /******************************************************************************* |
| * 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; |
| } |
| } |