blob: 4dc79e2b0d5741b2c3711b7f03b8275da215e65a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 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
* QNX Software Systems - Mikhail Khodjaiants - Registers View (Bug 53640)
* Wind River - Pawel Piech - Drag/Drop to Expressions View (Bug 184057)
* Wind River - Pawel Piech - Busy status while updates in progress (Bug 206822)
* Wind River - Pawel Piech - NPE when closing the Variables view (Bug 213719)
* Wind River - Pawel Piech - Fix viewer input race condition (Bug 234908)
* Wind River - Anton Leherbauer - Fix selection provider (Bug 254442)
* Patrick Chuong (Texas Instruments) - Improve usability of the breakpoint view (Bug 238956)
* Patrick Chuong (Texas Instruments) and Pawel Piech (Wind River) -
* Allow multiple debug views and multiple debug context providers (Bug 327263)
*******************************************************************************/
package org.eclipse.debug.internal.ui.views.variables;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.DelegatingModelPresentation;
import org.eclipse.debug.internal.ui.IDebugHelpContextIds;
import org.eclipse.debug.internal.ui.LazyModelPresentation;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.debug.internal.ui.VariablesViewModelPresentation;
import org.eclipse.debug.internal.ui.actions.CollapseAllAction;
import org.eclipse.debug.internal.ui.actions.ConfigureColumnsAction;
import org.eclipse.debug.internal.ui.actions.variables.ChangeVariableValueAction;
import org.eclipse.debug.internal.ui.actions.variables.ShowTypesAction;
import org.eclipse.debug.internal.ui.actions.variables.ToggleDetailPaneAction;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
import org.eclipse.debug.internal.ui.viewers.model.VirtualFindAction;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewActionProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputRequestor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ViewerInputService;
import org.eclipse.debug.internal.ui.views.DebugModelPresentationContext;
import org.eclipse.debug.internal.ui.views.IDebugExceptionHandler;
import org.eclipse.debug.internal.ui.views.variables.details.AvailableDetailPanesAction;
import org.eclipse.debug.internal.ui.views.variables.details.DetailPaneProxy;
import org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer2;
import org.eclipse.debug.ui.AbstractDebugView;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.IDebugView;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.debug.ui.contexts.IDebugContextService;
import org.eclipse.e4.ui.services.IStylingEngine;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveListener;
import org.eclipse.ui.ISaveablePart2;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.handlers.CollapseAllHandler;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.IUpdate;
/**
* This view shows variables and their values for a particular stack frame
*/
public class VariablesView extends AbstractDebugView implements IDebugContextListener,
IPropertyChangeListener, IDebugExceptionHandler,
IPerspectiveListener, IModelChangedListener,
IViewerUpdateListener, IDetailPaneContainer2, ISaveablePart2 {
private static final String COLLAPSE_ALL = "CollapseAll"; //$NON-NLS-1$
/**
* Unique ID of variables view tree viewer used for CSS styling
*/
private static final String CSS_VARIABLES_VIEWER_ID = "VariablesViewer"; //$NON-NLS-1$
/**
* Selection provider wrapping an exchangeable active selection provider. Sends
* out a selection changed event when the active selection provider changes.
* Forwards all selection changed events of the active selection provider.
*/
private static class SelectionProviderWrapper implements ISelectionProvider {
private final ListenerList<ISelectionChangedListener> fListenerList = new ListenerList<>(ListenerList.IDENTITY);
private final ISelectionChangedListener fListener = this::fireSelectionChanged;
private ISelectionProvider fActiveProvider;
private SelectionProviderWrapper(ISelectionProvider provider) {
setActiveProvider(provider);
}
private void setActiveProvider(ISelectionProvider provider) {
if (fActiveProvider == provider || this == provider) {
return;
}
if (fActiveProvider != null) {
fActiveProvider.removeSelectionChangedListener(fListener);
}
if (provider != null) {
provider.addSelectionChangedListener(fListener);
}
fActiveProvider = provider;
fireSelectionChanged(new SelectionChangedEvent(this, getSelection()));
}
private void dispose() {
fListenerList.clear();
setActiveProvider(null);
}
private void fireSelectionChanged(SelectionChangedEvent event) {
for (ISelectionChangedListener iSelectionChangedListener : fListenerList) {
iSelectionChangedListener.selectionChanged(event);
}
}
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
fListenerList.add(listener);
}
@Override
public ISelection getSelection() {
if (fActiveProvider != null) {
return fActiveProvider.getSelection();
}
return StructuredSelection.EMPTY;
}
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
fListenerList.remove(listener);
}
@Override
public void setSelection(ISelection selection) {
if (fActiveProvider != null) {
fActiveProvider.setSelection(selection);
}
}
}
/**
* The model presentation used as the label provider for the tree viewer,
* and also as the detail information provider for the detail pane.
*/
protected VariablesViewModelPresentation fModelPresentation;
/**
* The UI construct that provides a sliding sash between the variables tree
* and the detail pane.
*/
private SashForm fSashForm;
/**
* Composite that holds the details pane and always remains
*/
private Composite fDetailsAnchor;
/**
* Composite that holds the separator container and detail pane control.
* Gets disposed/created as the layout changes.
*/
private Composite fDetailsComposite;
/**
* Separator used when detail pane background colors of tree/detail pane are different.
*/
private Label fSeparator;
/**
* Parent of the viewer, used to detect re-sizing for automatic layout
*/
private Composite fParent;
/**
* Whether the detail pane has been built yet.
*/
private boolean fPaneBuilt = false;
/**
* The detail pane that displays detailed information about the current selection
* @since 3.3
*/
private DetailPaneProxy fDetailPane;
/**
* Stores whether the tree viewer was the last control to have focus in the
* view. Used to give focus to the correct component if the user leaves the view.
* @since 3.3
*/
private boolean fTreeHasFocus = true;
/**
* Various listeners used to update the enabled state of actions and also to
* populate the detail pane.
*/
private ISelectionChangedListener fTreeSelectionChangedListener;
/**
* Listener added to the control of the detail pane, allows view to keep track of which
* part last had focus, the tree or the detail pane.
*/
private Listener fDetailPaneActivatedListener;
/**
* Viewer input service used to translate active debug context to viewer input.
*/
private ViewerInputService fInputService;
private Map<String, IAction> fGlobalActionMap = new HashMap<>();
/**
* Viewer input requester used to update the viewer once the viewer input has been
* resolved.
*/
private IViewerInputRequestor fRequester = update -> {
if (!update.isCanceled()) {
viewerInputUpdateComplete(update);
}
};
/**
* These are used to initialize and persist the position of the sash that
* separates the tree viewer from the detail pane.
*/
private static final int[] DEFAULT_SASH_WEIGHTS = {13, 6};
private int[] fLastSashWeights;
private boolean fToggledDetailOnce;
private String fCurrentDetailPaneOrientation = IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_HIDDEN;
private ToggleDetailPaneAction[] fToggleDetailPaneActions;
private ConfigureColumnsAction fConfigureColumnsAction;
protected String PREF_STATE_MEMENTO = "pref_state_memento."; //$NON-NLS-1$
public static final String LOGICAL_STRUCTURE_TYPE_PREFIX = "VAR_LS_"; //$NON-NLS-1$
/**
* Presentation context property.
* @since 3.3
*/
public static final String PRESENTATION_SHOW_LOGICAL_STRUCTURES = "PRESENTATION_SHOW_LOGICAL_STRUCTURES"; //$NON-NLS-1$
/**
* the preference name for the view part of the sash form
* @since 3.2
*/
protected static final String SASH_VIEW_PART = DebugUIPlugin.getUniqueIdentifier() + ".SASH_VIEW_PART"; //$NON-NLS-1$
/**
* the preference name for the details part of the sash form
* @since 3.2
*/
protected static final String SASH_DETAILS_PART = DebugUIPlugin.getUniqueIdentifier() + ".SASH_DETAILS_PART"; //$NON-NLS-1$
/**
* Sash weights for a specific detail pane type
*/
protected static final String DETAIL_PANE_TYPE = "DETAIL_PANE_TYPE"; //$NON-NLS-1$
/**
* Visits deltas to determine if details should be displayed
*/
static class Visitor implements IModelDeltaVisitor {
/**
* Whether to trigger details display.
*
* @since 3.3
*/
private boolean fTriggerDetails = false;
@Override
public boolean visit(IModelDelta delta, int depth) {
if ((delta.getFlags() & IModelDelta.CONTENT) > 0) {
fTriggerDetails = true;
return false;
}
return true;
}
public void reset() {
fTriggerDetails = false;
}
public boolean isTriggerDetails() {
return fTriggerDetails;
}
}
/**
* Delta visitor
*/
private Visitor fVisitor = new Visitor();
/**
* Job to update details in the UI thread.
*/
private Job fTriggerDetailsJob = new UIJob("trigger details") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
refreshDetailPaneContents();
return Status.OK_STATUS;
}
};
/**
* Selection provider registered with the view site.
*/
private SelectionProviderWrapper fSelectionProvider;
/**
* Presentation context for this view.
*/
private IPresentationContext fPresentationContext;
private Font variablesTreeFont;
private TreeModelViewer variablesViewer;
/**
* Remove myself as a selection listener
* and preference change listener.
*
* @see IWorkbenchPart#dispose()
*/
@Override
public void dispose() {
DebugUITools.removePartDebugContextListener(getSite(), this);
getSite().getWorkbenchWindow().removePerspectiveListener(this);
DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
JFaceResources.getFontRegistry().removeListener(this);
TreeModelViewer viewer = getVariablesViewer();
if (viewer != null) {
viewer.removeModelChangedListener(this);
viewer.removeViewerUpdateListener(this);
}
if (fPresentationContext != null) {
fPresentationContext.dispose();
fPresentationContext = null;
}
if (fDetailPane != null) {
fDetailPane.dispose();
}
fInputService.dispose();
fSelectionProvider.dispose();
if (variablesTreeFont != null) {
variablesTreeFont.dispose();
}
super.dispose();
}
/**
* Called when the viewer input update is completed. Unlike
* {@link #setViewerInput(Object)}, it allows overriding classes
* to examine the context for which the update was calculated.
*
* @param update Completed viewer input update.
*/
protected void viewerInputUpdateComplete(IViewerInputUpdate update) {
setViewerInput(update.getInputElement());
updateAction(FIND_ACTION);
}
/**
* Sets the input to the viewer
* @param context the object context
*/
protected void setViewerInput(Object context) {
if (context == null) {
// Clear the detail pane
refreshDetailPaneContents();
}
Object current = getViewer().getInput();
if (current == null && context == null) {
return;
}
if (current != null && current.equals(context)) {
return;
}
showViewer();
getViewer().setInput(context);
updateObjects();
}
@Override
public void propertyChange(PropertyChangeEvent event) {
String propertyName= event.getProperty();
if (propertyName.equals(IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR) ||
propertyName.equals(IDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND) ||
propertyName.equals(IDebugUIConstants.PREF_VARIABLE_TEXT_FONT)) {
Viewer viewer = getViewer();
if (viewer == variablesViewer) {
setVariablesViewerTreeFont(variablesViewer);
}
viewer.refresh();
}
}
@Override
public Viewer createViewer(Composite parent) {
addResizeListener(parent);
fParent = parent;
fTriggerDetailsJob.setSystem(true);
// create the sash form that will contain the tree viewer & text viewer
fSashForm = new SashForm(parent, SWT.NONE);
getModelPresentation();
DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
JFaceResources.getFontRegistry().addListener(this);
variablesViewer = createTreeViewer(fSashForm);
fInputService = new ViewerInputService(variablesViewer, fRequester);
fSashForm.setMaximizedControl(variablesViewer.getControl());
setVariablesViewerTreeFont(variablesViewer);
fDetailsAnchor = SWTFactory.createComposite(fSashForm, parent.getFont(), 1, 1, GridData.FILL_BOTH, 0, 0);
fSashForm.setWeights(getLastSashWeights());
fSelectionProvider = new SelectionProviderWrapper(variablesViewer);
getSite().setSelectionProvider(fSelectionProvider);
createOrientationActions(variablesViewer);
IPreferenceStore prefStore = DebugUIPlugin.getDefault().getPreferenceStore();
String orientation = prefStore.getString(getDetailPanePreferenceKey());
for (ToggleDetailPaneAction action : fToggleDetailPaneActions) {
action.setChecked(action.getOrientation().equals(orientation));
}
fDetailPane = new DetailPaneProxy(this);
fDetailPane.addProperyListener((source, propId) -> firePropertyChange(propId));
setDetailPaneOrientation(orientation);
IMemento memento = getMemento();
if (memento != null) {
variablesViewer.initState(memento);
}
variablesViewer.addModelChangedListener(this);
variablesViewer.addViewerUpdateListener(this);
initDragAndDrop(variablesViewer);
return variablesViewer;
}
private void setVariablesViewerTreeFont(TreeModelViewer variablesViewer) {
Tree tree = variablesViewer.getTree();
FontDescriptor fontDescriptor = JFaceResources.getFontDescriptor(IDebugUIConstants.PREF_VARIABLE_TEXT_FONT);
Font oldVariablesTreeFont = variablesTreeFont;
variablesTreeFont = fontDescriptor.createFont(tree.getDisplay());
tree.setFont(variablesTreeFont); // needed when themes are disabled
if (oldVariablesTreeFont != null) {
oldVariablesTreeFont.dispose();
}
IStylingEngine engine = PlatformUI.getWorkbench().getService(IStylingEngine.class);
if (engine != null) {
engine.setId(tree, CSS_VARIABLES_VIEWER_ID);
}
}
/**
* Initializes the drag and/or drop adapters for this view. Called from createViewer().
*
* @param viewer the viewer to add drag/drop support to.
* @since 3.4
*/
protected void initDragAndDrop(TreeModelViewer viewer) {
// Drag only
viewer.addDragSupport(DND.DROP_COPY, new Transfer[] {LocalSelectionTransfer.getTransfer()}, new SelectionDragAdapter(viewer));
}
@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
super.init(site, memento);
PREF_STATE_MEMENTO = PREF_STATE_MEMENTO + site.getId();
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
String string = store.getString(PREF_STATE_MEMENTO);
if(string.length() > 0) {
try (ByteArrayInputStream bin = new ByteArrayInputStream(string.getBytes()); InputStreamReader reader = new InputStreamReader(bin)) {
XMLMemento stateMemento = XMLMemento.createReadRoot(reader);
setMemento(stateMemento);
} catch (WorkbenchException e) {
} catch (IOException e1) {
}
}
IMemento mem = getMemento();
// check the weights to makes sure they are valid -- bug 154025
setLastSashWeights(DEFAULT_SASH_WEIGHTS);
if (mem != null) {
int[] weights = getWeights(mem);
if (weights != null) {
setLastSashWeights(weights);
}
}
site.getWorkbenchWindow().addPerspectiveListener(this);
}
/**
* Returns sash weights stored in the given memento or <code>null</code> if none.
*
* @param memento Memento to read sash weights from
* @return sash weights or <code>null</code>
*/
private int[] getWeights(IMemento memento) {
Integer sw = memento.getInteger(SASH_VIEW_PART);
if(sw != null) {
int view = sw.intValue();
sw = memento.getInteger(SASH_DETAILS_PART);
if(sw != null) {
int details = sw.intValue();
if(view > -1 && details > -1) {
return new int[] {view, details};
}
}
}
return null;
}
@Override
public void partDeactivated(IWorkbenchPart part) {
String id = part.getSite().getId();
if (id.equals(getSite().getId())) {
try (ByteArrayOutputStream bout = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(bout)) {
XMLMemento memento = XMLMemento.createWriteRoot("VariablesViewMemento"); //$NON-NLS-1$
saveViewerState(memento);
memento.save(writer);
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
String xmlString = bout.toString();
store.putValue(PREF_STATE_MEMENTO, xmlString);
} catch (IOException e) {
}
}
super.partDeactivated(part);
}
/**
* Saves the current state of the viewer
* @param memento the memento to write the viewer state into
*/
public void saveViewerState(IMemento memento) {
if (fSashForm != null && !fSashForm.isDisposed()) {
int[] weights = fSashForm.getWeights();
memento.putInteger(SASH_VIEW_PART, weights[0]);
memento.putInteger(SASH_DETAILS_PART, weights[1]);
}
getVariablesViewer().saveState(memento);
}
/**
* @return the pref key for the variables view details pane
*/
protected String getDetailPanePreferenceKey() {
return IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_ORIENTATION;
}
/**
* Create and return the main tree viewer that displays variable.
* @param parent Viewer's parent control
* @return The created viewer.
*/
protected TreeModelViewer createTreeViewer(Composite parent) {
int style = getViewerStyle();
fPresentationContext = new DebugModelPresentationContext(getPresentationContextId(), this, fModelPresentation);
final TreeModelViewer variablesViewer = new TreeModelViewer(parent, style, fPresentationContext);
variablesViewer.getControl().addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
fTreeHasFocus = true;
fSelectionProvider.setActiveProvider(variablesViewer);
setGlobalActions();
}
@Override
public void focusLost(FocusEvent e){
// Do not reset the selection provider with the provider proxy.
// This should allow toolbar actions to remain active when the view
// is de-activated but still visible.
// Bug 316850.
clearGlobalActions();
getViewSite().getActionBars().updateActionBars();
}
});
variablesViewer.getPresentationContext().addPropertyChangeListener(
event -> {
if (IPresentationContext.PROPERTY_COLUMNS.equals(event.getProperty())) {
IAction action = getAction("ShowTypeNames"); //$NON-NLS-1$
if (action != null) {
action.setEnabled(event.getNewValue() == null);
}
}
});
variablesViewer.addPostSelectionChangedListener(getTreeSelectionChangedListener());
DebugUITools.addPartDebugContextListener(getSite(), this);
return variablesViewer;
}
private void setGlobalActions() {
for (Entry<String, IAction> entry : fGlobalActionMap.entrySet()) {
String actionID = entry.getKey();
IAction action = getOverrideAction(actionID);
if (action == null) {
action = entry.getValue();
}
setAction(actionID, action);
}
getViewSite().getActionBars().updateActionBars();
}
/**
* Save the global actions from action bar so they are not overridden by the
* detail pane.
*/
@Override
protected void createContextMenu(Control menuControl) {
super.createContextMenu(menuControl);
IActionBars actionBars = getViewSite().getActionBars();
if (!fGlobalActionMap.containsKey(SELECT_ALL_ACTION)) {
setGlobalAction(IDebugView.SELECT_ALL_ACTION, actionBars.getGlobalActionHandler(SELECT_ALL_ACTION));
}
if (!fGlobalActionMap.containsKey(COPY_ACTION)) {
setGlobalAction(COPY_ACTION, actionBars.getGlobalActionHandler(COPY_ACTION));
}
if (!fGlobalActionMap.containsKey(PASTE_ACTION)) {
setGlobalAction(PASTE_ACTION, actionBars.getGlobalActionHandler(PASTE_ACTION));
}
}
private void clearGlobalActions() {
for (String id : fGlobalActionMap.keySet()) {
setAction(id, null);
}
getViewSite().getActionBars().updateActionBars();
}
/**
* Returns the active debug context for this view based on the view's
* site IDs.
*
* @return Active debug context for this view.
*
* @since 3.7
*/
protected ISelection getDebugContext() {
IViewSite site = (IViewSite)getSite();
IDebugContextService contextService = DebugUITools.getDebugContextManager().getContextService(site.getWorkbenchWindow());
return contextService.getActiveContext(site.getId(), site.getSecondaryId());
}
/**
* Returns the presentation context id for this view.
*
* @return context id
*/
protected String getPresentationContextId() {
return IDebugUIConstants.ID_VARIABLE_VIEW;
}
/**
* Returns the presentation context secondary id for this view.
*
* @return context secondary id.
*/
protected String getPresentationContextSecondaryId() {
return ((IViewSite)getSite()).getSecondaryId();
}
/**
* Returns the style bits for the viewer.
*
* @return SWT style
*/
protected int getViewerStyle() {
return SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION;
}
@Override
protected String getHelpContextId() {
return IDebugHelpContextIds.VARIABLE_VIEW;
}
private void addResizeListener(Composite parent) {
parent.addControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent e) {
}
@Override
public void controlResized(ControlEvent e) {
if (IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_AUTO.equals(fCurrentDetailPaneOrientation)) {
setDetailPaneOrientation(IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_AUTO);
}
}
});
}
/**
* Returns vertical or horizontal based on view size.
*
* @return vertical or horizontal
*/
int computeOrientation() {
Point size= fParent.getSize();
if (size.x != 0 && size.y != 0) {
if ((size.x / 3)> size.y) {
return SWT.HORIZONTAL;
} else {
return SWT.VERTICAL;
}
}
return SWT.HORIZONTAL;
}
/**
* Set the orientation of the details pane so that is one of:
* - underneath the main tree view
* - to the right of the main tree view
* - not visible
* @param orientation Detail pane orientation to set.
*
* @see IDebugPreferenceConstants#VARIABLES_DETAIL_PANE_AUTO
* @see IDebugPreferenceConstants#VARIABLES_DETAIL_PANE_HIDDEN
* @see IDebugPreferenceConstants#VARIABLES_DETAIL_PANE_UNDERNEATH
*/
public void setDetailPaneOrientation(String orientation) {
if (!IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_AUTO.equals(orientation) && orientation.equals(fCurrentDetailPaneOrientation)) {
return;
}
fCurrentDetailPaneOrientation = orientation;
DebugUIPlugin.getDefault().getPreferenceStore().setValue(getDetailPanePreferenceKey(), orientation);
if (orientation.equals(IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_HIDDEN)) {
hideDetailPane();
} else {
int vertOrHoriz = -1;
if (orientation.equals(IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_AUTO)) {
vertOrHoriz = computeOrientation();
if (fPaneBuilt && fSashForm.getOrientation() == vertOrHoriz) {
showDetailPane();
return;
}
} else {
vertOrHoriz = orientation.equals(IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_UNDERNEATH) ? SWT.VERTICAL : SWT.HORIZONTAL;
}
buildDetailPane(vertOrHoriz);
revealTreeSelection();
}
}
private void buildDetailPane(int orientation) {
try {
fDetailsAnchor.setRedraw(false);
if (fDetailsComposite != null) {
fDetailPane.dispose();
fDetailsComposite.dispose();
}
fSashForm.setOrientation(orientation);
if (orientation == SWT.VERTICAL) {
fDetailsComposite = SWTFactory.createComposite(fDetailsAnchor, fDetailsAnchor.getFont(), 1, 1, GridData.FILL_BOTH, 0, 0);
GridLayout layout = (GridLayout) fDetailsComposite.getLayout();
layout.verticalSpacing = 0;
fSeparator = new Label(fDetailsComposite, SWT.SEPARATOR| SWT.HORIZONTAL);
fSeparator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
} else {
fDetailsComposite = SWTFactory.createComposite(fDetailsAnchor, fDetailsAnchor.getFont(), 2, 1, GridData.FILL_BOTH, 0, 0);
GridLayout layout = (GridLayout) fDetailsComposite.getLayout();
layout.horizontalSpacing = 0;
fSeparator= new Label(fDetailsComposite, SWT.SEPARATOR | SWT.VERTICAL);
fSeparator.setLayoutData(new GridData(SWT.TOP, SWT.FILL, false, true));
}
// force update so detail pane can adapt to orientation change
showDetailPane();
} finally {
fDetailsAnchor.layout(true);
fDetailsAnchor.setRedraw(true);
fPaneBuilt = true;
}
}
/**
* Hides the details pane
*/
private void hideDetailPane() {
if (fToggledDetailOnce) {
setLastSashWeights(fSashForm.getWeights());
}
fSashForm.setMaximizedControl(getViewer().getControl());
}
/**
* Shows the details pane
*/
private void showDetailPane() {
fSashForm.setMaximizedControl(null);
fSashForm.setWeights(getLastSashWeights());
refreshDetailPaneContents();
fToggledDetailOnce = true;
}
/**
* Make sure the currently selected item in the tree is visible.
*/
protected void revealTreeSelection() {
StructuredViewer viewer = (StructuredViewer) getViewer();
if (viewer != null) {
ISelection selection = viewer.getSelection();
if (selection instanceof IStructuredSelection) {
Object selected = ((IStructuredSelection)selection).getFirstElement();
if (selected != null) {
viewer.reveal(selected);
}
}
}
}
/**
* Return the relative weights that were in effect the last time both panes were
* visible in the sash form, or the default weights if:
* <ul>
* <li> both panes have not yet been made visible</li>
* <li> one of the values persisted before is an invalid value</li>
* </ul>
* @return The last sash weights.
*/
protected int[] getLastSashWeights() {
if (fLastSashWeights == null) {
fLastSashWeights = DEFAULT_SASH_WEIGHTS;
}
//check the weights to makes sure they are valid -- bug 154025
else if(fLastSashWeights[0] < 0 || fLastSashWeights[1] < 0) {
fLastSashWeights = DEFAULT_SASH_WEIGHTS;
}
return fLastSashWeights;
}
/**
* Set the current relative weights of the controls in the sash form, so that
* the sash form can be reset to this layout at a later time.
* @param weights Weight to add.
*/
protected void setLastSashWeights(int[] weights) {
fLastSashWeights = weights;
}
@Override
protected void createActions() {
IAction action = new ShowTypesAction(this);
setAction("ShowTypeNames",action); //$NON-NLS-1$
action = new ToggleLogicalStructureAction(this);
setAction("ToggleContentProviders", action); //$NON-NLS-1$
action = new CollapseAllAction((TreeModelViewer)getViewer());
setAction(COLLAPSE_ALL, action);
IHandlerService hs = getSite().getService(IHandlerService.class);
if (hs != null) {
hs.activateHandler(CollapseAllHandler.COMMAND_ID, new ActionHandler(action));
}
action = new ChangeVariableValueAction(this);
action.setEnabled(false);
setAction("ChangeVariableValue", action); //$NON-NLS-1$
action= new VirtualFindAction(getVariablesViewer());
setGlobalAction(FIND_ACTION, action);
}
/**
* Adds the given action to the set of global actions managed by this
* variables view. Global actions are cleared and reset whenever the detail
* pane is activated to allow the detail pane to set the actions as
* well.
*
* @param actionID Action ID that the given action implements.
* @param action Action implementation.
*
* @since 3.8
*/
protected void setGlobalAction(String actionID, IAction action) {
fGlobalActionMap.put(actionID, action);
}
@Override
public IAction getAction(String actionID) {
// Check if model overrides the action. Global action overrides are
// checked in setGlobalActions() so skip them here.
if (!fGlobalActionMap.containsKey(actionID)) {
IAction overrideAction = getOverrideAction(actionID);
if (overrideAction != null) {
return overrideAction;
}
}
return super.getAction(actionID);
}
private IAction getOverrideAction(String actionID) {
Viewer viewer = getViewer();
if (viewer != null) {
IViewActionProvider actionProvider = (IViewActionProvider) DebugPlugin.getAdapter(
viewer.getInput(), IViewActionProvider.class);
if (actionProvider != null) {
IAction action = actionProvider.getAction(getPresentationContext(), actionID);
if (action != null) {
return action;
}
}
}
return null;
}
@Override
public void updateObjects() {
super.updateObjects();
if (fTreeHasFocus) {
setGlobalActions();
}
}
/**
* Creates the actions that allow the orientation of the detail pane to be changed.
*
* @param viewer Viewer to create actions for.
*/
private void createOrientationActions(TreeModelViewer viewer) {
IActionBars actionBars = getViewSite().getActionBars();
IMenuManager viewMenu = actionBars.getMenuManager();
fToggleDetailPaneActions = new ToggleDetailPaneAction[4];
fToggleDetailPaneActions[0] = new ToggleDetailPaneAction(this, IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_UNDERNEATH, null);
fToggleDetailPaneActions[1] = new ToggleDetailPaneAction(this, IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_RIGHT, null);
fToggleDetailPaneActions[2] = new ToggleDetailPaneAction(this, IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_AUTO, null);
fToggleDetailPaneActions[3] = new ToggleDetailPaneAction(this, IDebugPreferenceConstants.VARIABLES_DETAIL_PANE_HIDDEN, getToggleActionLabel());
viewMenu.add(new Separator());
final MenuManager layoutSubMenu = new MenuManager(VariablesViewMessages.VariablesView_40);
layoutSubMenu.setRemoveAllWhenShown(true);
layoutSubMenu.add(fToggleDetailPaneActions[0]);
layoutSubMenu.add(fToggleDetailPaneActions[1]);
layoutSubMenu.add(fToggleDetailPaneActions[2]);
layoutSubMenu.add(fToggleDetailPaneActions[3]);
viewMenu.add(layoutSubMenu);
viewMenu.add(new Separator());
fConfigureColumnsAction = new ConfigureColumnsAction(viewer);
setAction("ToggleColmns", new ToggleShowColumnsAction(viewer)); //$NON-NLS-1$
layoutSubMenu.addMenuListener(manager -> {
layoutSubMenu.add(fToggleDetailPaneActions[0]);
layoutSubMenu.add(fToggleDetailPaneActions[1]);
layoutSubMenu.add(fToggleDetailPaneActions[2]);
layoutSubMenu.add(fToggleDetailPaneActions[3]);
layoutSubMenu.add(new Separator());
IAction action = getAction("ToggleColmns"); //$NON-NLS-1$
((IUpdate)action).update();
if (action.isEnabled()) {
layoutSubMenu.add(action);
}
fConfigureColumnsAction.update();
if (fConfigureColumnsAction.isEnabled()) {
layoutSubMenu.add(fConfigureColumnsAction);
}
});
}
/**
* Returns the label to use for the action that toggles the view layout to be the tree viewer only (detail pane is hidden).
* Should be of the style '[view name] View Only'.
*
* @return action label for toggling the view layout to tree viewer only
*/
protected String getToggleActionLabel(){
return VariablesViewMessages.VariablesView_41;
}
/**
* Configures the toolBar.
*
* @param tbm The toolbar that will be configured
*/
@Override
protected void configureToolBar(IToolBarManager tbm) {
tbm.add(new Separator(this.getClass().getName()));
tbm.add(new Separator(IDebugUIConstants.RENDER_GROUP));
tbm.add(getAction("ShowTypeNames")); //$NON-NLS-1$
tbm.add(getAction("ToggleContentProviders")); //$NON-NLS-1$
tbm.add(getAction(COLLAPSE_ALL));
}
/**
* Adds items to the tree viewer's context menu including any extension defined
* actions.
*
* @param menu The menu to add the item to.
*/
@Override
protected void fillContextMenu(IMenuManager menu) {
menu.add(new Separator(IDebugUIConstants.EMPTY_VARIABLE_GROUP));
menu.add(new Separator(IDebugUIConstants.VARIABLE_GROUP));
menu.add(getAction(FIND_ACTION));
ChangeVariableValueAction changeValueAction = (ChangeVariableValueAction)getAction("ChangeVariableValue"); //$NON-NLS-1$
if (changeValueAction.isApplicable()) {
menu.add(changeValueAction);
}
menu.add(new Separator());
IAction action = new AvailableLogicalStructuresAction(this);
if (action.isEnabled()) {
menu.add(action);
}
action = new AvailableDetailPanesAction(this);
if (isDetailPaneVisible() && action.isEnabled()) {
menu.add(action);
}
menu.add(new Separator(IDebugUIConstants.EMPTY_RENDER_GROUP));
menu.add(new Separator(IDebugUIConstants.EMPTY_NAVIGATION_GROUP));
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
/**
* Lazily instantiate and return a selection listener that populates the detail pane,
* but only if the detail is currently visible.
*
* @return Created selection listener
*/
protected ISelectionChangedListener getTreeSelectionChangedListener() {
if (fTreeSelectionChangedListener == null) {
fTreeSelectionChangedListener = event -> {
if (event.getSelectionProvider().equals(getViewer())) {
clearStatusLine();
// if the detail pane is not visible, don't waste time retrieving details
if (fSashForm.getMaximizedControl() == getViewer().getControl()) {
return;
}
refreshDetailPaneContents();
treeSelectionChanged(event);
}
};
}
return fTreeSelectionChangedListener;
}
/**
* Selection in the variable tree changed. Perform any updates.
*
* @param event
*/
protected void treeSelectionChanged(SelectionChangedEvent event) {}
@Override
public String getCurrentPaneID() {
return fDetailPane.getCurrentPaneID();
}
@Override
public IStructuredSelection getCurrentSelection() {
if (getViewer() != null){
return (IStructuredSelection)getViewer().getSelection();
}
return null;
}
@Override
public Composite getParentComposite() {
return fDetailsComposite;
}
@Override
public IWorkbenchPartSite getWorkbenchPartSite() {
return getSite();
}
@Override
public void refreshDetailPaneContents() {
if (isDetailPaneVisible()) {
String currentPaneID = getCurrentPaneID();
if (currentPaneID != null && !fSashForm.isDisposed()) {
fLastSashWeights = fSashForm.getWeights();
}
fDetailPane.display(getCurrentSelection());
// Use a grey (widget background) sash for the detail pane normally
// If the detail pane is also grey, add a seperator line
Control control = fDetailPane.getCurrentControl();
if (control.getBackground().equals(fSashForm.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND))) {
fSashForm.setBackground(fSashForm.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
fSeparator.setVisible(true);
fSeparator.setBackground(control.getBackground());
} else {
fSashForm.setBackground(fSashForm.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
fSeparator.setVisible(false);
}
}
}
@Override
public void paneChanged(String newPaneID) {
if (fDetailPaneActivatedListener == null){
fDetailPaneActivatedListener = event -> fTreeHasFocus = false;
}
fDetailPane.getCurrentControl().addListener(SWT.Activate, fDetailPaneActivatedListener);
}
/**
* @return the model presentation to be used for this view
*/
protected IDebugModelPresentation getModelPresentation() {
if (fModelPresentation == null) {
fModelPresentation = new VariablesViewModelPresentation();
}
return fModelPresentation;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> required) {
if (IDebugModelPresentation.class.equals(required)) {
return (T) getModelPresentation();
}
else if (fDetailPane != null){
Object adapter = fDetailPane.getAdapter(required);
if (adapter != null) {
return (T) adapter;
}
}
return super.getAdapter(required);
}
/**
* If possible, calls the update method of the action associated with the given ID.
*
* @param actionId the ID of the action to update
*/
protected void updateAction(String actionId) {
IAction action= getAction(actionId);
if (action == null) {
action = fGlobalActionMap.get(actionId);
}
if (action instanceof IUpdate) {
((IUpdate) action).update();
}
}
/**
* @return whether the detail pane is visible to the user
*/
protected boolean isDetailPaneVisible() {
return !fToggleDetailPaneActions[3].isChecked();
}
@Override
protected Control getDefaultControl() {
return fSashForm;
}
@Override
public void handleException(DebugException e) {
showMessage(e.getMessage());
}
@Override
public void debugContextChanged(DebugContextEvent event) {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
contextActivated(event.getContext());
}
}
/**
* Updates actions and sets the viewer input when a context is activated.
* @param selection New selection to activate.
*/
protected void contextActivated(ISelection selection) {
if (!isAvailable() || !isVisible()) {
return;
}
if (selection instanceof IStructuredSelection) {
Object source = ((IStructuredSelection)selection).getFirstElement();
fInputService.resolveViewerInput(source);
}
}
/**
* Delegate to the <code>DOUBLE_CLICK_ACTION</code>, if any.
*/
@Override
public void doubleClick(DoubleClickEvent event) {
IAction action = getAction(DOUBLE_CLICK_ACTION);
if (action != null && action.isEnabled()) {
action.run();
} else {
ISelection selection = getVariablesViewer().getSelection();
if (selection instanceof TreeSelection) {
TreeSelection ss = (TreeSelection) selection;
if (ss.size() == 1) {
Widget item = getVariablesViewer().findItem(ss.getPaths()[0]);
if (item instanceof TreeItem) {
TreeItem ti = (TreeItem) item;
if (ti.getExpanded()) {
ti.setExpanded(false);
} else {
// need to trigger proper children updates when expanding
getVariablesViewer().expandToLevel(ss.getPaths()[0], 1);
}
}
}
}
}
}
@Override
public IDebugModelPresentation getPresentation(String id) {
if (getViewer() instanceof StructuredViewer) {
IDebugModelPresentation lp = getModelPresentation();
if (lp instanceof DelegatingModelPresentation) {
return ((DelegatingModelPresentation)lp).getPresentation(id);
}
if (lp instanceof LazyModelPresentation) {
if (((LazyModelPresentation)lp).getDebugModelIdentifier().equals(id)) {
return lp;
}
}
}
return null;
}
public boolean isMainViewerAvailable() {
return isAvailable();
}
/**
* @return the presentation context of the viewer
*/
protected IPresentationContext getPresentationContext() {
return getVariablesViewer().getPresentationContext();
}
/**
* Sets whether logical structures are being displayed
* @param flag If true, turns the logical structures on.
*/
public void setShowLogicalStructure(boolean flag) {
getPresentationContext().setProperty(PRESENTATION_SHOW_LOGICAL_STRUCTURES, Boolean.valueOf(flag));
}
/**
* Returns whether logical structures are being displayed
* @return Returns true if logical structures should be shown.
*/
public boolean isShowLogicalStructure() {
Boolean show = (Boolean) getPresentationContext().getProperty(PRESENTATION_SHOW_LOGICAL_STRUCTURES);
return show != null && show.booleanValue();
}
@Override
protected void becomesHidden() {
fInputService.resolveViewerInput(ViewerInputService.NULL_INPUT);
super.becomesHidden();
}
@Override
protected void becomesVisible() {
super.becomesVisible();
ISelection selection = getDebugContext();
contextActivated(selection);
}
/**
* @return the tree model viewer displaying variables
*/
protected TreeModelViewer getVariablesViewer() {
return (TreeModelViewer) getViewer();
}
/**
* Clears the status line of all messages and errors
*/
protected void clearStatusLine() {
IStatusLineManager manager = getViewSite().getActionBars().getStatusLineManager();
manager.setErrorMessage(null);
manager.setMessage(null);
}
@Override
public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {}
@Override
public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
if(changeId.equals(IWorkbenchPage.CHANGE_RESET)) {
setLastSashWeights(DEFAULT_SASH_WEIGHTS);
fSashForm.setWeights(DEFAULT_SASH_WEIGHTS);
fSashForm.layout(true);
}
}
@Override
public void modelChanged(IModelDelta delta, IModelProxy proxy) {
fVisitor.reset();
delta.accept(fVisitor);
updateAction(FIND_ACTION);
updateAction(COLLAPSE_ALL);
}
@Override
public void updateComplete(IViewerUpdate update) {
IStatus status = update.getStatus();
if (!update.isCanceled()) {
if (status != null && !status.isOK()) {
showMessage(status.getMessage());
} else {
showViewer();
}
if (TreePath.EMPTY.equals(update.getElementPath())) {
updateAction(FIND_ACTION);
updateAction(COLLAPSE_ALL);
}
}
}
@Override
public void updateStarted(IViewerUpdate update) {
}
@Override
public synchronized void viewerUpdatesBegin() {
fTriggerDetailsJob.cancel();
IWorkbenchSiteProgressService progressService =
getSite().getAdapter(IWorkbenchSiteProgressService.class);
if (progressService != null) {
progressService.incrementBusy();
}
}
@Override
public synchronized void viewerUpdatesComplete() {
if (fVisitor.isTriggerDetails()) {
fTriggerDetailsJob.schedule();
}
IWorkbenchSiteProgressService progressService =
getSite().getAdapter(IWorkbenchSiteProgressService.class);
if (progressService != null) {
progressService.decrementBusy();
}
}
@Override
public void setFocus() {
boolean success = false;
if (!fTreeHasFocus && fDetailPane != null){
success = fDetailPane.setFocus();
}
// Unless the detail pane successfully set focus to a control, set focus to the variables tree
if (!success && getViewer() != null){
getViewer().getControl().setFocus();
}
}
protected ToggleDetailPaneAction getToggleDetailPaneAction(String orientation)
{
for (ToggleDetailPaneAction action : fToggleDetailPaneActions) {
if (action.getOrientation().equals(orientation)) {
return action;
}
}
return null;
}
@Override
public void setSelectionProvider(ISelectionProvider provider) {
// Workaround for legacy detail pane implementations (bug 254442)
// set selection provider wrapper again in case it got overridden by detail pane
getSite().setSelectionProvider(fSelectionProvider);
// change active provider
fSelectionProvider.setActiveProvider(provider);
}
@Override
public void doSave(IProgressMonitor monitor) {
fDetailPane.doSave(monitor);
}
@Override
public void doSaveAs() {
fDetailPane.doSaveAs();
}
@Override
public boolean isDirty() {
return fDetailPane.isDirty();
}
@Override
public boolean isSaveAsAllowed() {
return fDetailPane.isSaveAsAllowed();
}
@Override
public boolean isSaveOnCloseNeeded() {
return fDetailPane.isSaveOnCloseNeeded();
}
@Override
public int promptToSaveOnClose() {
return ISaveablePart2.YES;
}
/**
* Stores the default value for the generated String that can be used as a
* key into a preference store based on the specified action.
*
* @param action the action to store a default value
* @param value the default value for action
*
*/
void storeDefaultPreference(Action action, Object value) {
if (value instanceof Boolean) {
getPreferenceStore().setDefault(generatePreferenceKey(action), (Boolean)value);
} else if (value instanceof String) {
getPreferenceStore().setDefault(generatePreferenceKey(action), (String) value);
}
}
}