blob: 368ae3243b8e649bea3acc6ab8ccb97d6ba6e81a [file] [log] [blame]
/*****************************************************************
* Copyright (c) 2009, 2013 Texas Instruments and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Patrick Chuong (Texas Instruments) - Initial API and implementation (Bug 238956)
* IBM Corporation - ongoing enhancements and bug fixing
*****************************************************************/
package org.eclipse.debug.internal.ui.views.breakpoints;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManagerListener;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IDebugHelpContextIds;
import org.eclipse.debug.internal.ui.VariablesViewModelPresentation;
import org.eclipse.debug.internal.ui.actions.breakpointGroups.PasteBreakpointsAction;
import org.eclipse.debug.internal.ui.actions.breakpointGroups.RemoveFromWorkingSetAction;
import org.eclipse.debug.internal.ui.actions.breakpoints.OpenBreakpointMarkerAction;
import org.eclipse.debug.internal.ui.actions.breakpoints.ShowTargetBreakpointsAction;
import org.eclipse.debug.internal.ui.actions.breakpoints.SkipAllBreakpointsAction;
import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointContainer;
import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointOrganizer;
import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointUIConstants;
import org.eclipse.debug.internal.ui.elements.adapters.DefaultBreakpointsViewInput;
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.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
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.ModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
import org.eclipse.debug.internal.ui.views.DebugUIViewsMessages;
import org.eclipse.debug.internal.ui.views.variables.VariablesView;
import org.eclipse.debug.internal.ui.views.variables.details.AvailableDetailPanesAction;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IBreakpointOrganizerDelegateExtension;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.SelectionListenerAction;
import org.eclipse.ui.operations.RedoActionHandler;
import org.eclipse.ui.operations.UndoActionHandler;
/**
* This class implements the breakpoints view.
*/
public class BreakpointsView extends VariablesView implements IBreakpointManagerListener {
private static final String ACTION_GOTO_MARKER = "GotoMarker"; //$NON-NLS-1$
private static final String ACTION_SKIP_BREAKPOINTS = "SkipBreakpoints"; //$NON-NLS-1$
private static final String ACTION_SHOW_MODEL_BREAKPOINT = "ShowBreakpointsForModel";//$NON-NLS-1$
private static final String ACTION_REMOVE_FROM_GROUP = "RemoveFromGroup"; //$NON-NLS-1$
private static final String KEY_VALUE = "value"; //$NON-NLS-1$
private Clipboard fClipboard;
private IBreakpointOrganizer[] fOrganizers;
/**
* Flag used to determine whether the viewer input is being set for the
* fist time. If this is the case the view contents are expanded.
* (bug 297762)
*/
private boolean fFirstInputSet = false;
private UndoActionHandler fUndoAction;
private RedoActionHandler fRedoAction;
@Override
public void dispose() {
if (fClipboard != null) {
fClipboard.dispose();
}
DebugPlugin.getDefault().getBreakpointManager().removeBreakpointManagerListener(this);
fUndoAction.dispose();
fRedoAction.dispose();
super.dispose();
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#getDetailPanePreferenceKey()
*/
@Override
protected String getDetailPanePreferenceKey() {
return IDebugPreferenceConstants.BREAKPOINTS_DETAIL_PANE_ORIENTATION;
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#getHelpContextId()
*/
@Override
protected String getHelpContextId() {
return IDebugHelpContextIds.BREAKPOINT_VIEW;
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#getViewerStyle()
*/
@Override
protected int getViewerStyle() {
return SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION | SWT.CHECK;
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#createViewer(org.eclipse.swt.widgets.Composite)
*/
@Override
public Viewer createViewer(Composite parent) {
TreeModelViewer viewer = (TreeModelViewer) super.createViewer(parent);
initBreakpointOrganizers(getMemento());
IPresentationContext presentationContext = viewer.getPresentationContext();
presentationContext.setProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ORGANIZERS, fOrganizers);
presentationContext.setProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ELEMENT_COMPARATOR, new ElementComparator(presentationContext));
return viewer;
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#getModelPresentation()
*/
@Override
protected IDebugModelPresentation getModelPresentation() {
if (fModelPresentation == null) {
fModelPresentation = new VariablesViewModelPresentation() {
/**
* Undo double slashes.
*/
@Override
public String getText(Object element) {
IDebugModelPresentation lp= getConfiguredPresentation(element);
if (lp != null) {
return lp.getText(element);
}
return getDefaultText(element);
}
};
}
return fModelPresentation;
}
/**
* Returns the tree model viewer.
* @return the backin gviewer
*/
public TreeModelViewer getTreeModelViewer() {
return (TreeModelViewer) getViewer();
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#configureToolBar(org.eclipse.jface.action.IToolBarManager)
*/
@Override
protected void configureToolBar(IToolBarManager tbm) {
tbm.add(new Separator(IDebugUIConstants.BREAKPOINT_GROUP));
tbm.add(getAction(ACTION_SHOW_MODEL_BREAKPOINT));
tbm.add(getAction(ACTION_GOTO_MARKER));
tbm.add(getAction(ACTION_SKIP_BREAKPOINTS));
tbm.add(new Separator(IDebugUIConstants.RENDER_GROUP));
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#fillContextMenu(org.eclipse.jface.action.IMenuManager)
*/
@Override
protected void fillContextMenu(IMenuManager menu) {
updateObjects();
menu.add(new Separator(IDebugUIConstants.EMPTY_NAVIGATION_GROUP));
menu.add(new Separator(IDebugUIConstants.NAVIGATION_GROUP));
menu.add(getAction(ACTION_GOTO_MARKER));
menu.add(new Separator(IDebugUIConstants.EMPTY_BREAKPOINT_GROUP));
menu.add(new Separator(IDebugUIConstants.BREAKPOINT_GROUP));
menu.add(getAction(PASTE_ACTION));
IAction action = getAction(ACTION_REMOVE_FROM_GROUP);
if (action != null && action.isEnabled()) {
menu.add(action);
}
menu.add(new Separator(IDebugUIConstants.EMPTY_RENDER_GROUP));
action = new AvailableDetailPanesAction(this);
if (isDetailPaneVisible() && action.isEnabled()) {
menu.add(action);
}
menu.add(new Separator(IDebugUIConstants.BREAKPOINT_GROUP_GROUP));
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#createActions()
*/
@Override
protected void createActions() {
IAction action = new OpenBreakpointMarkerAction(getViewer());
setAction(ACTION_GOTO_MARKER, action);
setAction(DOUBLE_CLICK_ACTION, action);
setAction(ACTION_SHOW_MODEL_BREAKPOINT, new ShowTargetBreakpointsAction(this));
SkipAllBreakpointsAction skipAll = new SkipAllBreakpointsAction(this);
setAction(ACTION_SKIP_BREAKPOINTS, skipAll);
skipAll.setActionDefinitionId(SkipAllBreakpointsAction.ACTION_DEFINITION_ID);
DebugPlugin.getDefault().getBreakpointManager().addBreakpointManagerListener(this);
fClipboard = new Clipboard(getSite().getShell().getDisplay());
PasteBreakpointsAction paste = new PasteBreakpointsAction(this);
setAction(PASTE_ACTION, paste);
paste.setActionDefinitionId(ActionFactory.PASTE.getCommandId());
//actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), paste);
setGlobalAction(PASTE_ACTION, paste);
getViewer().addSelectionChangedListener(paste);
paste.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_PASTE));
SelectionListenerAction remove = new RemoveFromWorkingSetAction(this);
setAction(ACTION_REMOVE_FROM_GROUP, remove);
getViewer().addSelectionChangedListener(remove);
IUndoContext undoContext= DebugUITools.getBreakpointsUndoContext();
fUndoAction= new UndoActionHandler(getSite(), undoContext);
fUndoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
fRedoAction= new RedoActionHandler(getSite(), undoContext);
fRedoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
//actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), fUndoAction);
//actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), fRedoAction);
setGlobalAction(ActionFactory.UNDO.getId(), fUndoAction);
setGlobalAction(ActionFactory.REDO.getId(), fRedoAction);
setGlobalAction(FIND_ACTION, new VirtualFindAction(getVariablesViewer()));
}
/*
* (non-Javadoc)
*
* @seeorg.eclipse.debug.internal.ui.views.variables.VariablesView#getToggleActionLabel()
*/
@Override
protected String getToggleActionLabel() {
return DebugUIViewsMessages.BreakpointsView_12;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#getPresentationContextId()
*/
@Override
protected String getPresentationContextId() {
return IDebugUIConstants.ID_BREAKPOINT_VIEW;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#contextActivated(org.eclipse.jface.viewers.ISelection)
*/
@Override
protected void contextActivated(ISelection selection) {
IPresentationContext presentationContext = getTreeModelViewer().getPresentationContext();
if (selection == null || selection.isEmpty()) {
Object input = new DefaultBreakpointsViewInput(presentationContext);
super.contextActivated(new StructuredSelection(input));
} else {
super.contextActivated(selection);
}
if (isAvailable() && isVisible()) {
updateAction("ContentAssist"); //$NON-NLS-1$
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#setViewerInput(java.lang.Object)
*/
@Override
protected void setViewerInput(Object context) {
Object current = getViewer().getInput();
if (current == null && context == null) {
return;
}
if (current != null && current.equals(context)) {
return;
}
showViewer();
getViewer().setInput(context);
// Expand all elements when the view is first shown. (bug 297762)
if (!fFirstInputSet) {
fFirstInputSet = true;
expandAllElementsInViewer();
}
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#viewerInputUpdateComplete(org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate)
*/
@Override
protected void viewerInputUpdateComplete(IViewerInputUpdate update) {
// handles non-standard debug model
IStatus status = update.getStatus();
if ( (status == null || status.isOK()) && update.getElement() != null) {
setViewerInput(update.getInputElement());
} else {
setViewerInput(new DefaultBreakpointsViewInput(getTreeModelViewer().getPresentationContext()));
}
}
/**
* Returns whether this view is currently tracking the selection from the debug view.
*
* @return whether this view is currently tracking the debug view's selection
*/
public boolean isTrackingSelection() {
final TreeModelViewer viewer = getTreeModelViewer();
if (viewer != null) {
return Boolean.TRUE.equals(
viewer.getPresentationContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_TRACK_SELECTION) );
}
return false;
}
/**
* Sets whether this view should track the selection from the debug view.
*
* @param trackSelection whether or not this view should track the debug view's selection.
*/
public void setTrackSelection(boolean trackSelection) {
// set the track selection property for non-standard model to track the debug context
final TreeModelViewer viewer = getTreeModelViewer();
if (viewer != null) {
viewer.getPresentationContext().setProperty(
IBreakpointUIConstants.PROP_BREAKPOINTS_TRACK_SELECTION,
trackSelection ? Boolean.TRUE : Boolean.FALSE);
}
}
/**
* Initializes the persisted breakpoints organizers.
* @param memento the memento to read
*/
private void initBreakpointOrganizers(IMemento memento) {
if (memento != null) {
IMemento node = memento.getChild(IDebugUIConstants.EXTENSION_POINT_BREAKPOINT_ORGANIZERS);
if (node == null) {
fOrganizers = null;
} else {
String value = node.getString(KEY_VALUE);
if (value != null) {
String[] ids = value.split(","); //$NON-NLS-1$
BreakpointOrganizerManager manager = BreakpointOrganizerManager.getDefault();
List<IBreakpointOrganizer> organziers = new ArrayList<IBreakpointOrganizer>();
for (int i = 0; i < ids.length; i++) {
IBreakpointOrganizer organizer = manager.getOrganizer(ids[i]);
if (organizer != null) {
organziers.add(organizer);
}
}
fOrganizers = organziers.toArray(new IBreakpointOrganizer[organziers.size()]);
for (int i = 0; i < fOrganizers.length; i++) {
fOrganizers[i].addPropertyChangeListener(this);
}
}
}
}
}
/**
* Initializes drag and drop for the breakpoints viewer
* @param viewer the viewer to add drag and drop support to
*/
@Override
protected void initDragAndDrop(TreeModelViewer viewer) {
int ops = DND.DROP_MOVE | DND.DROP_COPY;
// drop
viewer.addDropSupport(ops, new Transfer[] {LocalSelectionTransfer.getTransfer()}, new BreakpointsDropAdapter(viewer, this));
// Drag
viewer.addDragSupport(ops, new Transfer[] {LocalSelectionTransfer.getTransfer()}, new BreakpointsDragAdapter(viewer, this));
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.VariablesView#saveViewerState(org.eclipse.ui.IMemento)
*/
@Override
public void saveViewerState(IMemento memento) {
StringBuffer buffer = new StringBuffer();
if (fOrganizers != null) {
for (int i = 0; i < fOrganizers.length; i++) {
IBreakpointOrganizer organizer = fOrganizers[i];
buffer.append(organizer.getIdentifier());
if (i < (fOrganizers.length - 1)) {
buffer.append(',');
}
}
IMemento node = memento.createChild(IDebugUIConstants.EXTENSION_POINT_BREAKPOINT_ORGANIZERS);
node.putString(KEY_VALUE, buffer.toString());
}
super.saveViewerState(memento);
}
/**
* Preserves the selection.
*
* @param selection the selection
*/
public void preserveSelection(IStructuredSelection selection) {
if (selection instanceof ITreeSelection && !selection.isEmpty()) {
TreePath path = ((ITreeSelection) selection).getPaths()[0];
TreeItem item = (TreeItem) ((TreeModelViewer) getViewer()).findItem(path);
Object toselect = null;
TreeItem[] siblings = null;
if (item != null) {
TreeItem parent = item.getParentItem();
if (parent != null) {
siblings = parent.getItems();
} else {
siblings = item.getParent().getItems();
}
if (siblings.length > 1) {
for (int i = 0; i < siblings.length; i++) {
if (item.equals(siblings[i])) {
if (i + 1 >= siblings.length) {
toselect = siblings[i - 1].getData();
break;
} else {
toselect = siblings[i + 1].getData();
break;
}
}
}
}
}
if (toselect != null) {
getViewer().setSelection(new StructuredSelection(toselect),true);
}
}
}
/**
* Sets the breakpoint organizers for this view.
*
* @param organizers the organizers, can be <code>null</code>.
*/
public void setBreakpointOrganizers(IBreakpointOrganizer[] organizers) {
fOrganizers = organizers;
TreeModelViewer viewer = getTreeModelViewer();
if (viewer != null) {
// update the presentation context organizer
viewer.getPresentationContext().setProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ORGANIZERS, fOrganizers);
}
System.out.println();
}
/**
* Sets the breakpoint filter for this view.
* @param filter the selection to act as a filter
*/
public void setFilterSelection(boolean filter) {
TreeModelViewer viewer = getTreeModelViewer();
if (viewer != null) {
// update the presentation context filter
viewer.getPresentationContext().setProperty(
IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION, filter ? Boolean.TRUE : Boolean.FALSE);
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.IBreakpointManagerListener#breakpointManagerEnablementChanged(boolean)
*/
@Override
public void breakpointManagerEnablementChanged(boolean enabled) {
DebugUIPlugin.getStandardDisplay().asyncExec(new Runnable() {
@Override
public void run() {
IAction action = getAction(ACTION_SKIP_BREAKPOINTS);
if (action != null) {
((SkipAllBreakpointsAction) action).updateActionCheckedState();
}
}
});
}
/**
* Expands all elements in the viewer.
*/
public void expandAllElementsInViewer() {
Display display = getSite().getShell().getDisplay();
final VirtualTreeModelViewer virtualViewer = new VirtualTreeModelViewer(
display, 0, ((ITreeModelViewer)getViewer()).getPresentationContext());
virtualViewer.setAutoExpandLevel(-1);
virtualViewer.addViewerUpdateListener(new IViewerUpdateListener() {
@Override
public void viewerUpdatesComplete() {
ModelDelta stateDelta = new ModelDelta(virtualViewer.getInput(), IModelDelta.NO_CHANGE);
virtualViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.EXPAND);
ITreeModelViewer treeModelViewer = ((ITreeModelViewer) getViewer());
if (treeModelViewer != null) {
((ITreeModelViewer) getViewer()).updateViewer(stateDelta);
}
virtualViewer.dispose();
}
@Override
public void viewerUpdatesBegin() {}
@Override
public void updateStarted(IViewerUpdate update) {}
@Override
public void updateComplete(IViewerUpdate update) {}
});
virtualViewer.setInput(getViewer().getInput());
}
/**
* Returns the breakpoint organizers for this view.
*
* @return the breakpoint organizers.
*/
public IBreakpointOrganizer[] getBreakpointOrganizers() {
return fOrganizers;
}
/**
* Returns whether the given selection can be pasted into the given target.
* <p>
* Scheme:
* <ul>
* <li>Breakpoints can only be pasted into allowable containers (i..e. like workings sets)</li>
* <li>Breakpoints can only be pasted into containers that they do not already reside in</li>
* <li>Breakpoints can only be pasted into containers, not other breakpoints</li>
* </ul>
* </p>
*
* @param target target of the paste
* @param selection the selection to paste
* @return whether the given selection can be pasted into the given target
*
* TODO Remove in favor of using <code>TreeItem</code>s and <code>TreePath</code>s to determine paste targets
*/
public boolean canPaste(Object target, ISelection selection) {
if(!(target instanceof IBreakpointContainer) || !(selection instanceof IStructuredSelection)) {
return false;
}
if(selection == null || selection.isEmpty()) {
return false;
}
IStructuredSelection ss = (IStructuredSelection) selection;
IBreakpointContainer container = (IBreakpointContainer) target;
for (Iterator<?> iter = ss.iterator(); iter.hasNext();) {
IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(iter.next(), IBreakpoint.class);
if (breakpoint == null || container.contains(breakpoint) || !container.getOrganizer().canAdd(breakpoint, container.getCategory())) {
return false;
}
}
return true;
}
/**
* Pastes the selection into the given target
*
* @param target target of the paste, either a IBreakpointContainer,
* or a Breakpoint within a IBreakpointContainer
* @param selection breakpoints
* @return whether successful
*
* TODO remove in favor of using <code>TreeItem</code> as paste target
*/
public boolean performPaste(Object target, ISelection selection) {
if (target instanceof IBreakpointContainer && selection instanceof IStructuredSelection) {
IBreakpointContainer container = (IBreakpointContainer) target;
Object[] objects = ((IStructuredSelection)selection).toArray();
for (int i = 0; i < objects.length; i++) {
IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(objects[i], IBreakpoint.class);
if (breakpoint != null) {
container.getOrganizer().addBreakpoint(breakpoint, container.getCategory());
}
}
return true;
}
return false;
}
/**
* Returns the container from within the specified path that is the container the breakpoint can be removed from
* @param path the path to get the container from
* @return the first found container that includes the breakpoint that allows removal, or <code>null</code> if none found
* @since 3.3
*/
public IBreakpointContainer getRemovableContainer(TreePath path) {
if (path != null) {
IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(path.getLastSegment(), IBreakpoint.class);
if (breakpoint != null) {
IBreakpointContainer container = null;
for(int i = path.getSegmentCount()-2; i > -1; i--) {
Object segment = path.getSegment(i);
if (segment instanceof IBreakpointContainer) {
container = (IBreakpointContainer) segment;
if(container.contains(breakpoint) &&
container.getOrganizer() != null &&
container.getOrganizer().canRemove(breakpoint, container.getCategory())) {
return container;
}
}
}
}
}
return null;
}
/**
* Returns the addable breakpoint container of the specified tree path
* @param path the path to get the container for
* @return the first found addable container for the specified tree path or <code>null</code> if none found
* @since 3.3
*/
protected IBreakpointContainer getAddableContainer(TreePath path) {
if (path != null) {
Object element = path.getLastSegment();
if (element instanceof IBreakpointContainer) {
return (IBreakpointContainer)element;
}
IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(element, IBreakpoint.class);
if (breakpoint != null) {
IBreakpointContainer container = null;
for (int i = path.getSegmentCount()-2; i > -1; i--) {
Object segment = path.getSegment(i);
if (segment instanceof IBreakpointContainer) {
container = (IBreakpointContainer) segment;
if (container.contains(breakpoint) && container.getOrganizer().canAdd(breakpoint, container.getCategory())) {
return container;
}
}
}
}
}
return null;
}
/**
* This method is used to determine if there is an addable parent container available for the specified drop target.
* <p>
* A drop target can be either a <code>IBreakpointContainer</code> or an <code>IBreakpoint</code>. This method always checks the entire hierarchy
* of the tree path for the specified target in the event one of the parent element does not support dropping.
* </p>
* @param path the path
* @param breakpoint the breakpoint
* @return <code>true</code> if there is a parent container available for the drop target <code>false</code> otherwise
*/
private boolean checkAddableParentContainers(TreePath path, IBreakpoint breakpoint) {
if (path != null) {
Object element = null;
for (int i = path.getSegmentCount()-1; i > -1; i--) {
element = path.getSegment(i);
if (element instanceof IBreakpointContainer) {
IBreakpointContainer container = (IBreakpointContainer) element;
if (container.contains(breakpoint) || !container.getOrganizer().canAdd(breakpoint, container.getCategory())) {
return false;
}
}
}
}
return true;
}
/**
* Returns if the selected item in the tree can be dragged
* <p>
* Scheme:
* <ul>
* <li>breakpoint containers cannot be dragged</li>
* <li>breakpoints can be dragged iff the container they reside in supports the removal of breakpoints</li>
* </ul>
* </p>
* @param items the tree paths to check if they can be dragged
* @return true if the selected element can be dragged, false otherwise
* @since 3.3
*/
boolean canDrag(TreePath[] items) {
if(items == null) {
return false;
}
if (items.length == 0) {
return false;
}
for (int i = 0; i < items.length; i++) {
if (getRemovableContainer(items[i]) == null) {
return false;
}
}
return true;
}
/**
* Performs the actual removal of breakpoints from their respective (removable) containers on a successful drag operation
* @param paths the tree paths to drag
* @since 3.3
*/
void performDrag(TreePath[] paths) {
if (paths == null) {
return;
}
Map<IBreakpointContainer, List<IBreakpoint>> containersToBreakpoints = new HashMap<IBreakpointContainer, List<IBreakpoint>>();
for (int i = 0; i < paths.length; i++) {
IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(paths[i].getLastSegment(), IBreakpoint.class);
if (breakpoint != null) {
IBreakpointContainer container = getRemovableContainer(paths[i]);
if(container != null) {
List<IBreakpoint> list = containersToBreakpoints.get(container);
if (list == null) {
list = new ArrayList<IBreakpoint>();
containersToBreakpoints.put(container, list);
}
list.add(breakpoint);
}
}
}
for (Entry<IBreakpointContainer, List<IBreakpoint>> entry : containersToBreakpoints.entrySet()) {
IBreakpointContainer container = entry.getKey();
List<IBreakpoint> list = entry.getValue();
IBreakpointOrganizer organizer = container.getOrganizer();
IBreakpoint[] breakpoints = list.toArray(new IBreakpoint[list.size()]);
if (organizer instanceof IBreakpointOrganizerDelegateExtension) {
IBreakpointOrganizerDelegateExtension extension = (IBreakpointOrganizerDelegateExtension) organizer;
extension.removeBreakpoints(breakpoints, container.getCategory());
} else {
for (int i = 0; i < breakpoints.length; i++) {
organizer.removeBreakpoint(breakpoints[i], container.getCategory());
}
}
}
}
/**
* Performs the actual addition of the selected breakpoints to the specified target
* @param target the target to add the selection of breakpoints to
* @param selection the selection of breakpoints
* @return true if the drop occurred, false otherwise
* @since 3.3
*/
protected boolean performDrop(TreePath target, ITreeSelection selection) {
if(target == null || selection == null) {
return false;
}
IBreakpointContainer container = getAddableContainer(target);
if (container == null) {
return false;
}
IBreakpointOrganizer organizer = container.getOrganizer();
List<IBreakpoint> breakpoints = new ArrayList<IBreakpoint>(selection.size());
for (Iterator<?> iter = selection.iterator(); iter.hasNext();) {
IBreakpoint breakpoint = (IBreakpoint) DebugPlugin.getAdapter(iter.next(), IBreakpoint.class);
if (breakpoint != null) {
breakpoints.add(breakpoint);
}
}
if (organizer instanceof IBreakpointOrganizerDelegateExtension) {
IBreakpointOrganizerDelegateExtension extension = (IBreakpointOrganizerDelegateExtension) organizer;
extension.addBreakpoints(
breakpoints.toArray(new IBreakpoint[breakpoints.size()]),
container.getCategory());
} else {
for (int i = 0; i < breakpoints.size(); i++) {
organizer.addBreakpoint(breakpoints.get(i), container.getCategory());
}
}
// TODO expandToLevel(target.getData(), ALL_LEVELS);
return true;
}
/**
* Determines if the specified element can be dropped into the specified target
* <p>
* Scheme:
* <ul>
* <li>Breakpoints can be dropped into working sets</li>
* <li>Breakpoints can be dropped into breakpoints, provided there is a drop-able parent of the target breakpoint</li>
* </ul>
* </p>
* @param target the target for the drop
* @param selection the selection to see if we can drop
* @return true if the specified element can be dropped into the specified target, false otherwise
* @since 3.3
*/
boolean canDrop(TreePath target, ITreeSelection selection) {
if (selection == null || target == null) {
return false;
}
for (Iterator<?> iter = selection.iterator(); iter.hasNext();) {
IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(iter.next(), IBreakpoint.class);
if (breakpoint == null || !checkAddableParentContainers(target, breakpoint)){
return false;
}
}
return true;
}
}