| /******************************************************************************* |
| * Copyright (c) 2012, 2014 Tilera Corporation and others. |
| * |
| * 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: |
| * William R. Swanson (Tilera Corporation) |
| * Marc Dumais (Ericsson) - Bug 399281 |
| * Marc Dumais (Ericsson) - Bug 441376 |
| *******************************************************************************/ |
| |
| package org.eclipse.cdt.visualizer.ui; |
| |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.cdt.visualizer.core.Extension; |
| import org.eclipse.cdt.visualizer.ui.events.IVisualizerViewerListener; |
| import org.eclipse.cdt.visualizer.ui.events.VisualizerViewerEvent; |
| import org.eclipse.cdt.visualizer.ui.plugin.CDTVisualizerUIPlugin; |
| import org.eclipse.cdt.visualizer.ui.util.ListenerList; |
| import org.eclipse.cdt.visualizer.ui.util.SelectionManager; |
| import org.eclipse.cdt.visualizer.ui.util.SelectionUtils; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.MenuDetectEvent; |
| import org.eclipse.swt.events.MenuDetectListener; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.part.PageBook; |
| |
| // ---------------------------------------------------------------------------- |
| // VisualizerViewer |
| // ---------------------------------------------------------------------------- |
| |
| /** |
| * CDT Visualizer Viewer class. |
| * |
| * This is the default implementation of IVisualizerViewer. |
| * It can also serve as a base class for custom visualizer viewers. |
| * |
| * The Visualizer Viewer is a simple container for multiple |
| * IVisualizers, where the currently selected IVisualizer |
| * determines which IVisualizer control is displayed in the viewer. |
| * |
| * The base implementation simply displays a single IVisualizer at a time. |
| * One can programmatically switch selections, but there are no user |
| * controls for doing so. (The intent is that derived types can add |
| * various kinds of switching controls, like a combo box, etc.) |
| */ |
| public class VisualizerViewer extends PageBook |
| implements IVisualizerViewer, MenuDetectListener, ISelectionProvider, ISelectionChangedListener { |
| // --- constants --- |
| |
| /** Extension point name for list of IVisualizer types. */ |
| public static final String VISUALIZER_EXTENSION_POINT_NAME = "visualizer"; //$NON-NLS-1$ |
| |
| // --- members --- |
| |
| /** Containing view. */ |
| protected VisualizerView m_view = null; |
| |
| /** Parent control. */ |
| protected Composite m_parent = null; |
| |
| /** List of registered visualizer types. */ |
| protected Map<String, IVisualizer> m_visualizers = null; |
| |
| /** Currently selected visualizer. */ |
| protected IVisualizer m_currentVisualizer = null; |
| |
| /** Event listeners. */ |
| protected ListenerList m_listeners = null; |
| |
| /** Viewer selection manager. */ |
| protected SelectionManager m_selectionManager = null; |
| |
| // --- constructors/destructors --- |
| |
| /** Constructor. */ |
| public VisualizerViewer(VisualizerView view, Composite parent) { |
| super(parent, SWT.NONE); |
| initVisualizerViewer(view, parent); |
| // so we're notified when the widget is disposed |
| addDisposeListener(new DisposeListener() { |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| dispose(); |
| } |
| }); |
| } |
| |
| /** Dispose method. */ |
| @Override |
| public void dispose() { |
| cleanupVisualizerViewer(); |
| super.dispose(); |
| } |
| |
| /** Overridden to permit subclassing of SWT component */ |
| @Override |
| protected void checkSubclass() { |
| // Do nothing. |
| // (Superclass implementation throws "Subclassing not allowed" exception.) |
| } |
| |
| // --- init methods --- |
| |
| /** Initializes control */ |
| protected void initVisualizerViewer(VisualizerView view, Composite parent) { |
| m_view = view; |
| m_parent = parent; |
| |
| // Event listener support |
| m_listeners = new ListenerList(this, "VisualizerViewer event listeners") { |
| @Override |
| public void raise(Object listener, Object event) { |
| if (listener instanceof IVisualizerViewerListener && event instanceof VisualizerViewerEvent) { |
| IVisualizerViewerListener typedListener = (IVisualizerViewerListener) listener; |
| VisualizerViewerEvent typedEvent = (VisualizerViewerEvent) event; |
| typedListener.visualizerEvent(VisualizerViewer.this, typedEvent); |
| } |
| } |
| }; |
| |
| // Selection change listener support |
| m_selectionManager = new SelectionManager(this, "Visualizer Viewer selection manager"); |
| |
| // Set default colors for empty viewer. |
| Display display = getDisplay(); |
| setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); |
| setForeground(display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND)); |
| |
| // Listen to paint event to draw "No visualizers" warning if needed. |
| this.addPaintListener(new PaintListener() { |
| @Override |
| public void paintControl(PaintEvent e) { |
| VisualizerViewer.this.paint(e.gc); |
| } |
| }); |
| |
| // Load any visualizers defined through extension point. |
| loadVisualizers(); |
| } |
| |
| /** Cleans up control */ |
| protected void cleanupVisualizerViewer() { |
| // perform any cleanup here |
| disposeVisualizers(); |
| if (m_selectionManager != null) { |
| m_selectionManager.dispose(); |
| m_selectionManager = null; |
| } |
| } |
| |
| // --- accessors --- |
| |
| /** Gets containing view. */ |
| @Override |
| public VisualizerView getView() { |
| return m_view; |
| } |
| |
| /** Returns non-localized unique name for selected visualizer. */ |
| @Override |
| public String getVisualizerName() { |
| String result = "visualizer"; |
| if (m_currentVisualizer != null) |
| result = m_currentVisualizer.getName(); |
| return result; |
| } |
| |
| /** Returns localized name to display for selected visualizer. */ |
| @Override |
| public String getVisualizerDisplayName() { |
| String result = "Visualizer"; |
| if (m_currentVisualizer != null) |
| result = m_currentVisualizer.getDisplayName(); |
| return result; |
| } |
| |
| /** Returns localized tooltip text to display for selected visualizer. */ |
| @Override |
| public String getVisualizerDescription() { |
| String result = "Visualizer"; |
| if (m_currentVisualizer != null) |
| result = m_currentVisualizer.getDescription(); |
| return result; |
| } |
| |
| // --- control management --- |
| |
| /** Gets viewer control. */ |
| @Override |
| public Control getControl() { |
| return this; |
| } |
| |
| // --- focus handling --- |
| |
| /** |
| * Invoked by VisualizerView when currently selected presentation, |
| * if any, should take the focus. |
| */ |
| @Override |
| public boolean setFocus() { |
| boolean result = false; |
| if (m_currentVisualizer != null) { |
| // Tell current visualizer's control to take the focus. |
| m_currentVisualizer.getControl().setFocus(); |
| } else { |
| // Otherwise, let viewer take the focus. |
| result = super.setFocus(); |
| } |
| return result; |
| } |
| |
| // --- viewer events --- |
| |
| /** Adds listener for viewer events. */ |
| @Override |
| public void addVisualizerViewerListener(IVisualizerViewerListener listener) { |
| m_listeners.addListener(listener); |
| } |
| |
| /** Removes listener for viewer events. */ |
| @Override |
| public void removeVisualizerViewerListener(IVisualizerViewerListener listener) { |
| m_listeners.removeListener(listener); |
| } |
| |
| /** Raises change event for all listeners. */ |
| public void raiseVisualizerChangedEvent() { |
| VisualizerViewerEvent event = new VisualizerViewerEvent(this, VisualizerViewerEvent.VISUALIZER_CHANGED); |
| m_listeners.raise(event); |
| } |
| |
| /** Raises context menu event for all listeners. */ |
| public void raiseContextMenuEvent(int x, int y) { |
| VisualizerViewerEvent event = new VisualizerViewerEvent(this, VisualizerViewerEvent.VISUALIZER_CONTEXT_MENU, x, |
| y); |
| m_listeners.raise(event); |
| } |
| |
| // --- visualizer management --- |
| |
| /** Loads initial set of visualizers and constructs viewer controls. */ |
| protected void loadVisualizers() { |
| // TODO: add error checking, logging for errors in extension declarations |
| // TODO: do we need to worry about this being called more than once? |
| |
| m_visualizers = new Hashtable<>(); |
| |
| List<Extension> visualizers = Extension.getExtensions(CDTVisualizerUIPlugin.FEATURE_ID, |
| VISUALIZER_EXTENSION_POINT_NAME); |
| |
| if (visualizers != null) { |
| for (Extension e : visualizers) { |
| String id = e.getAttribute("id"); //$NON-NLS-1$ |
| IVisualizer visualizerInstance = e.getClassAttribute(); |
| if (id != null && visualizerInstance != null) { |
| // Add visualizer's control to viewer's "pagebook" of controls. |
| visualizerInstance.setViewer(this); |
| visualizerInstance.initializeVisualizer(); |
| visualizerInstance.createControl(this); |
| m_visualizers.put(id, visualizerInstance); |
| } |
| } |
| } |
| |
| // select initial visualization |
| selectDefaultVisualizer(); |
| } |
| |
| /** Cleans up visualizers. */ |
| protected void disposeVisualizers() { |
| for (String id : m_visualizers.keySet()) { |
| IVisualizer v = m_visualizers.get(id); |
| Control c = v.getControl(); |
| c.dispose(); |
| v.disposeVisualizer(); |
| } |
| m_visualizers.clear(); |
| } |
| |
| /** Selects default visualizer. |
| * Default implementation displays visualizer that can handle |
| * the current selection. |
| */ |
| public void selectDefaultVisualizer() { |
| // fake a workbench selection changed event |
| updateVisualizerFromWorkbenchSelection(); |
| } |
| |
| /** Selects specified visualizer, makes its control visible. */ |
| public void selectVisualizer(IVisualizer visualizer) { |
| if (visualizer == null) |
| return; |
| if (visualizer == m_currentVisualizer) |
| return; |
| |
| if (m_currentVisualizer != null) { |
| // let the visualizer know it's being hidden |
| m_currentVisualizer.visualizerDeselected(); |
| |
| // stop listening for context menu events |
| m_currentVisualizer.getControl().removeMenuDetectListener(this); |
| |
| // stop listening for selection changed events |
| m_currentVisualizer.removeSelectionChangedListener(this); |
| } |
| |
| m_currentVisualizer = visualizer; |
| |
| if (m_currentVisualizer != null) { |
| // We've added visualizer's control in loadVisualizers(), |
| // so all we need to do here is select it. |
| showPage(visualizer.getControl()); |
| |
| // start listening for context menu events |
| m_currentVisualizer.getControl().addMenuDetectListener(this); |
| |
| // start listening for selection changed events |
| m_currentVisualizer.addSelectionChangedListener(this); |
| |
| // raise visualizer changed event, so view knows |
| // it should update tab name, toolbar, etc. |
| raiseVisualizerChangedEvent(); |
| |
| // make sure workbench knows about current visualizer selection |
| updateWorkbenchFromVisualizerSelection(); |
| |
| // no need to update visualizer from workbench selection, |
| // we already do that whenever the workbench selection changes |
| |
| // let the visualizer know it's been shown |
| m_currentVisualizer.visualizerSelected(); |
| } |
| } |
| |
| /** Gets current visualizer. */ |
| public IVisualizer getVisualizer() { |
| return m_currentVisualizer; |
| } |
| |
| // --- menu/toolbar management --- |
| |
| /** Invoked when visualizer is selected, to populate the toolbar. */ |
| @Override |
| public void populateToolBar(IToolBarManager toolBarManager) { |
| if (m_currentVisualizer != null) |
| m_currentVisualizer.populateToolBar(toolBarManager); |
| } |
| |
| /** Invoked when visualizer is selected, to populate the toolbar's menu. */ |
| @Override |
| public void populateMenu(IMenuManager menuManager) { |
| if (m_currentVisualizer != null) |
| m_currentVisualizer.populateMenu(menuManager); |
| } |
| |
| // --- context menu handling --- |
| |
| /** Invoked when context menu gesture happens on current |
| * visualizer control. |
| */ |
| @Override |
| public void menuDetected(MenuDetectEvent e) { |
| // raise event to allow view to show context menu |
| raiseContextMenuEvent(e.x, e.y); |
| } |
| |
| /** Invoked when context menu is about to be shown. */ |
| @Override |
| public void populateContextMenu(IMenuManager m) { |
| if (m_currentVisualizer != null) { |
| m_currentVisualizer.populateContextMenu(m); |
| } |
| } |
| |
| /** Gets context menu location. */ |
| @Override |
| public Point getContextMenuLocation() { |
| return m_view.getContextMenuLocation(); |
| } |
| |
| // --- paint methods --- |
| |
| /** |
| * Invoked when viewer needs to be repainted. |
| * May be overridden by derived classes. |
| * Default implementation displays "No visualizers defined." message |
| */ |
| public void paint(GC gc) { |
| gc.fillRectangle(getClientArea()); |
| if (m_visualizers == null || m_visualizers.size() == 0) { |
| String noVisualizersMessage = CDTVisualizerUIPlugin.getString("VisualizerViewer.no.visualizers.defined"); //$NON-NLS-1$ |
| gc.drawString("(" + noVisualizersMessage + ")", 10, 10); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| // --- ISelectionProvider implementation --- |
| |
| // Delegate to selection manager. |
| |
| /** Adds external listener for selection change events. */ |
| @Override |
| public void addSelectionChangedListener(ISelectionChangedListener listener) { |
| m_selectionManager.addSelectionChangedListener(listener); |
| } |
| |
| /** Removes external listener for selection change events. */ |
| @Override |
| public void removeSelectionChangedListener(ISelectionChangedListener listener) { |
| if (m_selectionManager != null) { |
| m_selectionManager.removeSelectionChangedListener(listener); |
| } |
| } |
| |
| /** Raises selection changed event. */ |
| public void raiseSelectionChangedEvent() { |
| m_selectionManager.raiseSelectionChangedEvent(); |
| } |
| |
| /** Gets current externally-visible selection. */ |
| @Override |
| public ISelection getSelection() { |
| return m_selectionManager.getSelection(); |
| } |
| |
| /** Sets externally-visible selection. */ |
| @Override |
| public void setSelection(ISelection selection) { |
| m_selectionManager.setSelection(selection); |
| } |
| |
| // --- workbench selection handling --- |
| |
| /** |
| * Updates visualizer from current workbench selection. |
| * NOTE: normally VisulizerView keeps the viewer in synch |
| * with the selection, so this should not need to be called |
| * except in special cases. |
| */ |
| public void updateVisualizerFromWorkbenchSelection() { |
| ISelection selection = SelectionUtils.getWorkbenchSelection(); |
| workbenchSelectionChanged(selection); |
| } |
| |
| /** |
| * Invoked by VisualizerView when workbench selection changes, |
| * and change's source is not this view. |
| * |
| * Selects visualizer (if any) that knows how to display current |
| * selection. Also invokes workbenchSelectionChanged() on visualizer |
| * so it can update itself accordingly. |
| */ |
| @Override |
| public void workbenchSelectionChanged(ISelection selection) { |
| // See if we need to change visualizers to handle selection type. |
| IVisualizer handles = null; |
| int weight = 0; |
| |
| // First, see if the current visualizer can handle the new selection. |
| // (This gives it automatic precedence if there's a tie.) |
| if (m_currentVisualizer != null) { |
| int w = m_currentVisualizer.handlesSelection(selection); |
| if (w > weight) { |
| handles = m_currentVisualizer; |
| weight = w; |
| } |
| } |
| |
| // Next, check the list of other visualizers, to see if any |
| // of them is more specific than the current one. |
| for (IVisualizer v : m_visualizers.values()) { |
| if (v == m_currentVisualizer) |
| continue; // already checked |
| int w = v.handlesSelection(selection); |
| if (w > weight) { |
| handles = v; |
| weight = w; |
| } |
| } |
| |
| // If NOBODY claims ownership, and we don't have a visualizer yet, |
| // then pick somebody and let them take a whack at it. |
| if (handles == null && m_visualizers.size() > 0) { |
| handles = m_visualizers.values().iterator().next(); |
| } |
| |
| // If we need to change visualizers, select the new one. |
| // Note: this also reports the new visualizer's selection to the workbench |
| if (handles != null && handles != m_currentVisualizer) { |
| selectVisualizer(handles); |
| } |
| |
| // Notify current visualizer that workbench selection has changed. |
| // (This means a visualizer is only notified of workbench selection changes |
| // if the selection is something it has reported that it can display.) |
| if (m_currentVisualizer != null) { |
| m_currentVisualizer.workbenchSelectionChanged(selection); |
| } |
| } |
| |
| // --- visualizer selection handling --- |
| |
| /** Updates workbench selection from current visualizer selection. |
| * Note: normally the viewer keeps the selection in synch, |
| * so you should not need to call this method except in special cases. |
| */ |
| public void updateWorkbenchFromVisualizerSelection() { |
| // fake a selection changed event |
| m_selectionManager.raiseSelectionChangedEvent(); |
| } |
| |
| /** Invoked when visualizer's selection changes. */ |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| // Publish changed visualizer selection to any listeners. |
| setSelection(event.getSelection()); |
| } |
| } |