/******************************************************************************* * Copyright (c) 2000, 2018 IBM 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.debug.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.DelegatingModelPresentation; import org.eclipse.debug.internal.ui.LazyModelPresentation; import org.eclipse.debug.internal.ui.actions.breakpoints.SkipAllBreakpointsAction; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.part.IPage; import org.eclipse.ui.part.MessagePage; import org.eclipse.ui.part.Page; import org.eclipse.ui.part.PageBook; import org.eclipse.ui.part.PageBookView; import org.eclipse.ui.texteditor.IUpdate; /** * Common function for views related to debugging. Clients implementing * views for a debugger should subclass this class. Common function * includes: *
IDebugView
IUpdate
* are updated when updateActions()
is
* called.REMOVE_ACTION
when the delete key
* is pressed.DOUBLE_CLICK_ACTION
when the mouse
* is double-clicked.PageBookView
mechanism.
* By default, a page book is created with a page showing
* this view's viewer. A message page is also created
* and shown when showMessage(String)
is
* called.becomesVisible()
and becomesHidden()
.getHelpContextId().
* This class may be sub-classed. *
* @since 2.0 */ public abstract class AbstractDebugView extends PageBookView implements IDebugView, IDoubleClickListener { /** * Underlying viewer that displays the contents of * this view. */ private Viewer fViewer = null; /** * This view's message page. */ private MessagePage fMessagePage = null; /** * Map of actions. Keys are strings, values * areIAction
.
*/
private MapIAction
.
*/
private Listnull
.
*/
private IMemento fMemento;
/**
* Whether this view is currently visible.
*/
private boolean fIsVisible = false;
/**
* The part listener for this view, used to notify this view when it
* becomes visible and hidden. Set to null
when this view isn't
* currently listening to part changes.
*/
private DebugViewPartListener fPartListener= null;
/**
* A message was requested to be displayed before the view was fully
* created. The message is cached until it can be properly displayed.
*/
private String fEarlyMessage= null;
private static SetREMOVE_ACTION
is invoked. Hooks help to
* this view. Subclasses must implement the following methods
* which are called in the following order when a view is
* created:createViewer(Composite)
- the context
* menu is hooked to the viewer's control.createActions()
configureToolBar(IToolBarManager)
getHelpContextId()
createViewer(Composite)
*/
protected abstract void createActions();
/**
* Returns this view's help context id, which is hooked
* to this view on creation.
*
* @return help context id
*/
protected abstract String getHelpContextId();
/**
* @see IWorkbenchPart#dispose()
*/
@Override
public void dispose() {
saveAllCheckedActionStates();
deregisterPartListener();
if (getViewer() instanceof StructuredViewer) {
((StructuredViewer)getViewer()).removeDoubleClickListener(this);
}
setViewer(null);
fActionMap.clear();
if (fMessagePage != null) {
fMessagePage.dispose();
fMessagePage = null;
}
super.dispose();
}
/**
* Saves the checked state for all actions contributed to the toolbar
* manager that function as a toggle action. The states are saved in
* the Debug UI plugin's preference store.
*
* @since 2.1
*/
protected void saveAllCheckedActionStates() {
IToolBarManager tbm= getViewSite().getActionBars().getToolBarManager();
IContributionItem[] items= tbm.getItems();
for (int i = 0; i < items.length; i++) {
IContributionItem iContributionItem = items[i];
if (iContributionItem instanceof ActionContributionItem) {
ActionContributionItem item= (ActionContributionItem)iContributionItem;
IAction action= item.getAction();
if (action.getStyle() == IAction.AS_CHECK_BOX && action.isEnabled()) {
saveCheckedActionState(action);
}
}
}
}
/**
* Save the checked state of the specified action in the Debug UI plugin's
* preference store. The specified action is expected to be enabled and
* support the style IAction.AS_CHECK_BOX
.
*
* @param action the enabled, toggle action whose checked state will be
* saved in preferences
* @since 2.1
*/
protected void saveCheckedActionState(IAction action) {
String prefKey = generatePreferenceKey(action);
IPreferenceStore prefStore = getPreferenceStore();
prefStore.setValue(prefKey, action.isChecked());
}
/**
* Generate a String that can be used as a key into a preference store based
* on the specified action. The resulting String will be unique across
* views.
* @param action the action to generate a key for
* @return a String suitable for use as a preference store key for the given
* action
* @since 2.1
*/
protected String generatePreferenceKey(IAction action) {
return getViewSite().getId() + '+' + action.getId();
}
/**
* Convenience method to return the preference store for the Debug UI
* plug-in.
*
* @return the preference store for the Debug UI plug-in
* @since 2.1
*/
protected IPreferenceStore getPreferenceStore() {
return DebugUIPlugin.getDefault().getPreferenceStore();
}
/**
* @see IDebugView#getViewer()
*/
@Override
public Viewer getViewer() {
return fViewer;
}
/**
* Returns this view's viewer as a structured viewer,
* or null
if none.
*
* @return this view's viewer as a structured viewer
* or null
*/
protected StructuredViewer getStructuredViewer() {
if (getViewer() instanceof StructuredViewer) {
return (StructuredViewer)getViewer();
}
return null;
}
/**
* Returns this view's viewer as a text viewer,
* or null
if none.
*
* @return this view's viewer as a text viewer
* or null
*/
protected TextViewer getTextViewer() {
if (getViewer() instanceof TextViewer) {
return (TextViewer)getViewer();
}
return null;
}
/**
* @see IDebugView#getPresentation(String)
*/
@Override
public IDebugModelPresentation getPresentation(String id) {
if (getViewer() instanceof StructuredViewer) {
IBaseLabelProvider lp = ((StructuredViewer)getViewer()).getLabelProvider();
if (lp instanceof DelegatingModelPresentation) {
return ((DelegatingModelPresentation)lp).getPresentation(id);
}
if (lp instanceof LazyModelPresentation) {
if (((LazyModelPresentation)lp).getDebugModelIdentifier().equals(id)) {
return (IDebugModelPresentation)lp;
}
}
}
return null;
}
/**
* Creates a pop-up menu on the given control. The menu
* is registered with this view's site, such that other
* plug-ins may contribute to the menu. Subclasses should
* call this method, specifying the menu control as the
* control used in their viewer (for example, tree viewer).
* Subclasses must implement the method
* #fillContextMenu(IMenuManager)
which will
* be called each time the context menu is realized.
*
* @param menuControl the control with which the pop-up
* menu will be associated with.
*/
protected void createContextMenu(Control menuControl) {
MenuManager menuMgr= new MenuManager("#PopUp"); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(mgr -> fillContextMenu(mgr));
Menu menu= menuMgr.createContextMenu(menuControl);
menuControl.setMenu(menu);
// register the context menu such that other plug-ins may contribute to it
if (getSite() != null) {
getSite().registerContextMenu(menuMgr, getViewer());
}
addContextMenuManager(menuMgr);
}
/**
* @see IDebugView#getContextMenuManager()
*
* @deprecated @see AbstractDebugView.getContextMenuManagers()
*/
@Deprecated
@Override
public IMenuManager getContextMenuManager() {
if (fContextMenuManagers != null) {
fContextMenuManagers.get(fContextMenuManagers.size() - 1);
}
return null;
}
/**
* Returns the context menu managers relevant to this view.
*
* @return the context menu managers relevant to this view
* @since 2.1
*/
public List#configureToolBar(IToolBarManager)
to
* contribute actions to the toolbar.
* * To properly initialize toggle actions that are contributed * to this view, state is restored for toggle actions that have * a persisted state in the Debug UI plugin's preferences. As well, any * toggle actions that have an initial state of 'checked' are invoked. The * actions' states are restored and the actions are invoked in a runnable, * after the view is created. *
*/ protected void initializeToolBar() { final IToolBarManager tbm= getViewSite().getActionBars().getToolBarManager(); configureToolBar(tbm); getViewSite().getActionBars().updateActionBars(); // This is done in a runnable to be run after this view's pane // is created Runnable r = () -> { if (!isAvailable()) { return; } IContributionItem[] items = tbm.getItems(); if (items != null) { for (int i = 0; i < items.length; i++) { if (items[i] instanceof ActionContributionItem) { IAction action = ((ActionContributionItem) items[i]).getAction(); if (!SkipAllBreakpointsAction.ACTION_ID.equals(action.getId())) { if (action.getStyle() == IAction.AS_CHECK_BOX) { initActionState(action); if (action.isChecked()) { action.run(); } } } } } setMemento(null); } updateObjects(); }; asyncExec(r); } /** * Restores the persisted checked state of the specified action that was * stored in preferences. If the action is disabled, its persisted state * is not restored (because a disabled action cannot be run). * * @param action the action whose checked state will be restored * @since 2.1 */ protected void initActionState(IAction action) { String id = action.getId(); if (id != null && action.isEnabled()) { String prefKey = generatePreferenceKey(action); boolean checked = getPreferenceStore().getBoolean(prefKey); action.setChecked(checked); } } /** * @see IViewPart#init(IViewSite, IMemento) */ @Override public void init(IViewSite site, IMemento memento) throws PartInitException { super.init(site, memento); //store the memento to be used when this view is created. setMemento(memento); } /** * Sets the viewer for this view. * * @param viewer viewer * @since 3.1 */ protected void setViewer(Viewer viewer) { fViewer = viewer; } /** * Subclasses implement this menu to contribute actions * to the toolbar. This method is called after *createActions()
.
*
* @param tbm the tool bar manager for this view's site
* @see #createViewer(Composite)
*/
protected abstract void configureToolBar(IToolBarManager tbm);
/**
* @see IDebugView#setAction(String, IAction)
*/
@Override
public void setAction(String actionID, IAction action) {
if (action == null) {
Object removedAction= fActionMap.remove(actionID);
fUpdateables.remove(removedAction);
} else {
fActionMap.put(actionID, action);
if (action instanceof IUpdate) {
add((IUpdate) action);
}
}
if (fgGlobalActionIds.contains(actionID)) {
IActionBars actionBars = getViewSite().getActionBars();
actionBars.setGlobalActionHandler(actionID, action);
}
}
/**
* @see IDebugView#getAction(String)
*/
@Override
public IAction getAction(String actionID) {
return fActionMap.get(actionID);
}
/**
* Updates all the registered updatables.
*/
public void updateObjects() {
for (IUpdate update : fUpdateables) {
update.update();
}
}
/**
* Handles key events in viewer. Invokes
* REMOVE_ACTION
when the delete
* key is pressedDOUBLE_CLICK_ACTION
,
* if any.
*
* @see IDoubleClickListener#doubleClick(DoubleClickEvent)
*/
@Override
public void doubleClick(DoubleClickEvent event) {
IAction action = getAction(DOUBLE_CLICK_ACTION);
if (action != null && !event.getSelection().isEmpty() && action.isEnabled()) {
action.run();
}
}
/**
* Registers the given runnable with the display
* associated with this view's control, if any.
* @param r the {@link Runnable} to run
*
* @see org.eclipse.swt.widgets.Display#asyncExec(java.lang.Runnable)
*/
public void asyncExec(Runnable r) {
if (isAvailable()) {
getControl().getDisplay().asyncExec(r);
}
}
/**
* Returns the control for this view, or null
if none.
*
* @return the control for this view, or null
if none
* @since 3.0
*/
protected Control getControl() {
return getViewer().getControl();
}
/**
* Registers the given runnable with the display
* associated with this view's control, if any.
* @param r the {@link Runnable} to run
*
* @see org.eclipse.swt.widgets.Display#syncExec(java.lang.Runnable)
*/
public void syncExec(Runnable r) {
if (isAvailable()) {
getControl().getDisplay().syncExec(r);
}
}
/**
* Returns the memento that contains the persisted state of
* the view. May be null
.
* @return the current {@link IMemento}
*/
protected IMemento getMemento() {
return fMemento;
}
/**
* Sets the memento that contains the persisted state of the
* view.
* @param memento the new {@link IMemento}
*/
protected void setMemento(IMemento memento) {
fMemento = memento;
}
/**
* Returns the specified view in this view's page
* or null
if none.
*
* @param id view identifier
* @return view part
*/
protected IViewPart findView(String id) {
IWorkbenchPage page = getSite().getPage();
IViewPart view = null;
if (page != null) {
view = page.findView(id);
}
return view;
}
/**
* @see PageBookView#isImportant(IWorkbenchPart)
*/
@Override
protected boolean isImportant(IWorkbenchPart part) {
return false;
}
/**
* @see PageBookView#doCreatePage(IWorkbenchPart)
*/
@Override
protected PageRec doCreatePage(IWorkbenchPart part) {
return null;
}
/**
* @see PageBookView#doDestroyPage(org.eclipse.ui.IWorkbenchPart, org.eclipse.ui.part.PageBookView.PageRec)
*/
@Override
protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
}
/**
* @see PageBookView#getBootstrapPart()
*/
@Override
protected IWorkbenchPart getBootstrapPart() {
return null;
}
/**
* Returns the default control for this view. By default,
* this view's viewer's control is returned. Subclasses
* should override if required - for example, if this
* view has its viewer nested inside other controls.
*
* @return this view's default control.
*/
protected Control getDefaultControl() {
Viewer viewer = getViewer();
if (viewer != null) {
return viewer.getControl();
}
return null;
}
/**
* Sets this view's message page
*
* @param page message page
*/
private void setMessagePage(MessagePage page) {
fMessagePage = page;
}
/**
* Returns this view's message page
*
* @return message page
*/
protected MessagePage getMessagePage() {
return fMessagePage;
}
/**
* Shows the given message in this view's message'
* page. Makes the message page the visible page.
*
* @param message the message to display
*/
public void showMessage(String message) {
if (getPageBook().isDisposed()) {
return;
}
if (getMessagePage() == null) {
//not fully created yet
fEarlyMessage= message;
return;
}
getMessagePage().setMessage(message);
getPageBook().showPage(getMessagePage().getControl());
}
/**
* Shows this view's viewer page.
*/
public void showViewer() {
if (getPageBook().isDisposed()) {
return;
}
getPageBook().showPage(getDefaultPage().getControl());
}
/**
* Returns whether this view's viewer is
* currently available.
*
* @return whether this view's viewer is
* currently available
*/
public boolean isAvailable() {
return !(getViewer() == null || getViewer().getControl() == null || getViewer().getControl().isDisposed());
}
/**
* @see IDebugView#add(IUpdate)
*/
@Override
public void add(IUpdate updatable) {
if (!fUpdateables.contains(updatable)) {
fUpdateables.add(updatable);
}
}
/**
* @see IDebugView#remove(IUpdate)
*/
@Override
public void remove(IUpdate updatable) {
fUpdateables.remove(updatable);
}
/**
* Adds a context menu manager that is relevant to this view.
* @param contextMenuManager The contextMenuManager to add
*
* @since 2.1
*/
public void addContextMenuManager(IMenuManager contextMenuManager) {
if (fContextMenuManagers == null) {
fContextMenuManagers = new ArrayList<>();
}
fContextMenuManagers.add(contextMenuManager);
}
/**
* Notification this view is now visible.
*
* @since 2.1
*/
protected void becomesVisible() {
}
/**
* Notification this view is now hidden.
*
* @since 2.1
*/
protected void becomesHidden() {
}
/**
* Returns whether this view is currently visible.
*
* @return whether this view is currently visible
* @since 2.1
*/
public boolean isVisible() {
return fIsVisible;
}
/**
* Creates and registers a part listener with this event handler's page,
* if one does not already exist.
*
* @since 2.1
*/
protected void registerPartListener() {
if (fPartListener == null) {
fPartListener= new DebugViewPartListener();
getSite().getPage().addPartListener(fPartListener);
}
}
/**
* Unregisters and disposes this event handler's part listener.
*
* @since 2.1
*/
protected void deregisterPartListener() {
if (fPartListener != null) {
getSite().getPage().removePartListener(fPartListener);
fPartListener = null;
}
}
/**
* Returns a map of the current attribute settings in the model
* presentation in this view associated with the given debug model.
* @param modelId the debug model identifier
* @return a map of the current attribute settings in the model
* presentation in this view associated with the given debug model
* @since 3.0
*/
public Map