1 /***************************************************************** 2 * Copyright (c) 2009, 2018 Texas Instruments and others 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Patrick Chuong (Texas Instruments) - Initial API and implementation (Bug 238956) 13 * IBM Corporation - ongoing enhancements and bug fixing 14 *****************************************************************/ 15 package org.eclipse.debug.internal.ui.views.breakpoints; 16 17 import java.util.ArrayList; 18 import java.util.HashMap; 19 import java.util.Iterator; 20 import java.util.List; 21 import java.util.Map; 22 import java.util.Map.Entry; 23 24 import org.eclipse.core.commands.operations.IUndoContext; 25 import org.eclipse.core.runtime.IStatus; 26 import org.eclipse.debug.core.DebugPlugin; 27 import org.eclipse.debug.core.IBreakpointManagerListener; 28 import org.eclipse.debug.core.model.IBreakpoint; 29 import org.eclipse.debug.internal.ui.DebugUIPlugin; 30 import org.eclipse.debug.internal.ui.IDebugHelpContextIds; 31 import org.eclipse.debug.internal.ui.VariablesViewModelPresentation; 32 import org.eclipse.debug.internal.ui.actions.breakpointGroups.PasteBreakpointsAction; 33 import org.eclipse.debug.internal.ui.actions.breakpointGroups.RemoveFromWorkingSetAction; 34 import org.eclipse.debug.internal.ui.actions.breakpoints.OpenBreakpointMarkerAction; 35 import org.eclipse.debug.internal.ui.actions.breakpoints.ShowTargetBreakpointsAction; 36 import org.eclipse.debug.internal.ui.actions.breakpoints.SkipAllBreakpointsAction; 37 import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointContainer; 38 import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointOrganizer; 39 import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointUIConstants; 40 import org.eclipse.debug.internal.ui.elements.adapters.DefaultBreakpointsViewInput; 41 import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; 42 import org.eclipse.debug.internal.ui.viewers.model.VirtualFindAction; 43 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; 44 import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; 45 import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 46 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; 47 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; 48 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; 49 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; 50 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; 51 import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; 52 import org.eclipse.debug.internal.ui.views.DebugUIViewsMessages; 53 import org.eclipse.debug.internal.ui.views.variables.VariablesView; 54 import org.eclipse.debug.internal.ui.views.variables.details.AvailableDetailPanesAction; 55 import org.eclipse.debug.ui.DebugUITools; 56 import org.eclipse.debug.ui.IBreakpointOrganizerDelegateExtension; 57 import org.eclipse.debug.ui.IDebugModelPresentation; 58 import org.eclipse.debug.ui.IDebugUIConstants; 59 import org.eclipse.jface.action.IAction; 60 import org.eclipse.jface.action.IMenuManager; 61 import org.eclipse.jface.action.IToolBarManager; 62 import org.eclipse.jface.action.Separator; 63 import org.eclipse.jface.util.LocalSelectionTransfer; 64 import org.eclipse.jface.viewers.ISelection; 65 import org.eclipse.jface.viewers.IStructuredSelection; 66 import org.eclipse.jface.viewers.ITreeSelection; 67 import org.eclipse.jface.viewers.StructuredSelection; 68 import org.eclipse.jface.viewers.TreePath; 69 import org.eclipse.jface.viewers.Viewer; 70 import org.eclipse.swt.SWT; 71 import org.eclipse.swt.dnd.Clipboard; 72 import org.eclipse.swt.dnd.DND; 73 import org.eclipse.swt.dnd.Transfer; 74 import org.eclipse.swt.widgets.Composite; 75 import org.eclipse.swt.widgets.Display; 76 import org.eclipse.swt.widgets.TreeItem; 77 import org.eclipse.ui.IMemento; 78 import org.eclipse.ui.ISharedImages; 79 import org.eclipse.ui.IWorkbenchActionConstants; 80 import org.eclipse.ui.IWorkbenchCommandConstants; 81 import org.eclipse.ui.PlatformUI; 82 import org.eclipse.ui.actions.ActionFactory; 83 import org.eclipse.ui.actions.SelectionListenerAction; 84 import org.eclipse.ui.operations.RedoActionHandler; 85 import org.eclipse.ui.operations.UndoActionHandler; 86 87 /** 88 * This class implements the breakpoints view. 89 */ 90 public class BreakpointsView extends VariablesView implements IBreakpointManagerListener { 91 private static final String ACTION_GOTO_MARKER = "GotoMarker"; //$NON-NLS-1$ 92 private static final String ACTION_SKIP_BREAKPOINTS = "SkipBreakpoints"; //$NON-NLS-1$ 93 private static final String ACTION_SHOW_MODEL_BREAKPOINT = "ShowBreakpointsForModel";//$NON-NLS-1$ 94 private static final String ACTION_REMOVE_FROM_GROUP = "RemoveFromGroup"; //$NON-NLS-1$ 95 96 97 private static final String KEY_VALUE = "value"; //$NON-NLS-1$ 98 99 private Clipboard fClipboard; 100 private IBreakpointOrganizer[] fOrganizers; 101 102 /** 103 * Flag used to determine whether the viewer input is being set for the 104 * fist time. If this is the case the view contents are expanded. 105 * (bug 297762) 106 */ 107 private boolean fFirstInputSet = false; 108 109 private UndoActionHandler fUndoAction; 110 private RedoActionHandler fRedoAction; 111 112 113 @Override dispose()114 public void dispose() { 115 if (fClipboard != null) { 116 fClipboard.dispose(); 117 } 118 DebugPlugin.getDefault().getBreakpointManager().removeBreakpointManagerListener(this); 119 120 fUndoAction.dispose(); 121 fRedoAction.dispose(); 122 123 super.dispose(); 124 } 125 126 @Override getDetailPanePreferenceKey()127 protected String getDetailPanePreferenceKey() { 128 return IDebugPreferenceConstants.BREAKPOINTS_DETAIL_PANE_ORIENTATION; 129 } 130 131 @Override getHelpContextId()132 protected String getHelpContextId() { 133 return IDebugHelpContextIds.BREAKPOINT_VIEW; 134 } 135 136 @Override getViewerStyle()137 protected int getViewerStyle() { 138 return SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION | SWT.CHECK; 139 } 140 141 @Override createViewer(Composite parent)142 public Viewer createViewer(Composite parent) { 143 TreeModelViewer viewer = (TreeModelViewer) super.createViewer(parent); 144 145 initBreakpointOrganizers(getMemento()); 146 147 IPresentationContext presentationContext = viewer.getPresentationContext(); 148 presentationContext.setProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ORGANIZERS, fOrganizers); 149 presentationContext.setProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ELEMENT_COMPARATOR, new ElementComparator(presentationContext)); 150 151 return viewer; 152 } 153 154 @Override getModelPresentation()155 protected IDebugModelPresentation getModelPresentation() { 156 if (fModelPresentation == null) { 157 fModelPresentation = new VariablesViewModelPresentation() { 158 /** 159 * Undo double slashes. 160 */ 161 @Override 162 public String getText(Object element) { 163 IDebugModelPresentation lp= getConfiguredPresentation(element); 164 if (lp != null) { 165 return lp.getText(element); 166 } 167 return getDefaultText(element); 168 } 169 }; 170 } 171 return fModelPresentation; 172 } 173 174 /** 175 * Returns the tree model viewer. 176 * @return the backin gviewer 177 */ getTreeModelViewer()178 public TreeModelViewer getTreeModelViewer() { 179 return (TreeModelViewer) getViewer(); 180 } 181 182 @Override configureToolBar(IToolBarManager tbm)183 protected void configureToolBar(IToolBarManager tbm) { 184 tbm.add(new Separator(IDebugUIConstants.BREAKPOINT_GROUP)); 185 tbm.add(getAction(ACTION_SHOW_MODEL_BREAKPOINT)); 186 tbm.add(getAction(ACTION_GOTO_MARKER)); 187 tbm.add(getAction(ACTION_SKIP_BREAKPOINTS)); 188 tbm.add(new Separator(IDebugUIConstants.RENDER_GROUP)); 189 } 190 191 @Override fillContextMenu(IMenuManager menu)192 protected void fillContextMenu(IMenuManager menu) { 193 updateObjects(); 194 menu.add(new Separator(IDebugUIConstants.EMPTY_NAVIGATION_GROUP)); 195 menu.add(new Separator(IDebugUIConstants.NAVIGATION_GROUP)); 196 menu.add(getAction(ACTION_GOTO_MARKER)); 197 menu.add(new Separator(IDebugUIConstants.EMPTY_BREAKPOINT_GROUP)); 198 menu.add(new Separator(IDebugUIConstants.BREAKPOINT_GROUP)); 199 menu.add(getAction(PASTE_ACTION)); 200 IAction action = getAction(ACTION_REMOVE_FROM_GROUP); 201 if (action != null && action.isEnabled()) { 202 menu.add(action); 203 } 204 menu.add(new Separator(IDebugUIConstants.EMPTY_RENDER_GROUP)); 205 action = new AvailableDetailPanesAction(this); 206 if (isDetailPaneVisible() && action.isEnabled()) { 207 menu.add(action); 208 } 209 menu.add(new Separator(IDebugUIConstants.BREAKPOINT_GROUP_GROUP)); 210 211 menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); 212 } 213 214 @Override createActions()215 protected void createActions() { 216 IAction action = new OpenBreakpointMarkerAction(getViewer()); 217 setAction(ACTION_GOTO_MARKER, action); 218 setAction(DOUBLE_CLICK_ACTION, action); 219 setAction(ACTION_SHOW_MODEL_BREAKPOINT, new ShowTargetBreakpointsAction(this)); 220 SkipAllBreakpointsAction skipAll = new SkipAllBreakpointsAction(this); 221 setAction(ACTION_SKIP_BREAKPOINTS, skipAll); 222 skipAll.setActionDefinitionId(SkipAllBreakpointsAction.ACTION_DEFINITION_ID); 223 DebugPlugin.getDefault().getBreakpointManager().addBreakpointManagerListener(this); 224 225 fClipboard = new Clipboard(getSite().getShell().getDisplay()); 226 227 PasteBreakpointsAction paste = new PasteBreakpointsAction(this); 228 setAction(PASTE_ACTION, paste); 229 paste.setActionDefinitionId(ActionFactory.PASTE.getCommandId()); 230 //actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), paste); 231 setGlobalAction(PASTE_ACTION, paste); 232 getViewer().addSelectionChangedListener(paste); 233 paste.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); 234 235 SelectionListenerAction remove = new RemoveFromWorkingSetAction(this); 236 setAction(ACTION_REMOVE_FROM_GROUP, remove); 237 getViewer().addSelectionChangedListener(remove); 238 239 IUndoContext undoContext= DebugUITools.getBreakpointsUndoContext(); 240 fUndoAction= new UndoActionHandler(getSite(), undoContext); 241 fUndoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO); 242 fRedoAction= new RedoActionHandler(getSite(), undoContext); 243 fRedoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO); 244 //actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), fUndoAction); 245 //actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), fRedoAction); 246 setGlobalAction(ActionFactory.UNDO.getId(), fUndoAction); 247 setGlobalAction(ActionFactory.REDO.getId(), fRedoAction); 248 setGlobalAction(FIND_ACTION, new VirtualFindAction(getVariablesViewer())); 249 } 250 251 @Override getToggleActionLabel()252 protected String getToggleActionLabel() { 253 return DebugUIViewsMessages.BreakpointsView_12; 254 } 255 256 @Override getPresentationContextId()257 protected String getPresentationContextId() { 258 return IDebugUIConstants.ID_BREAKPOINT_VIEW; 259 } 260 261 @Override contextActivated(ISelection selection)262 protected void contextActivated(ISelection selection) { 263 if (!isAvailable() || !isVisible()) { 264 return; 265 } 266 267 IPresentationContext presentationContext = getTreeModelViewer().getPresentationContext(); 268 269 if (selection == null || selection.isEmpty()) { 270 Object input = new DefaultBreakpointsViewInput(presentationContext); 271 super.contextActivated(new StructuredSelection(input)); 272 } else { 273 super.contextActivated(selection); 274 } 275 if (isAvailable() && isVisible()) { 276 updateAction("ContentAssist"); //$NON-NLS-1$ 277 } 278 } 279 280 @Override setViewerInput(Object context)281 protected void setViewerInput(Object context) { 282 Object current = getViewer().getInput(); 283 if (current == null && context == null) { 284 return; 285 } 286 287 if (current != null && current.equals(context)) { 288 return; 289 } 290 291 showViewer(); 292 getViewer().setInput(context); 293 294 // Expand all elements when the view is first shown. (bug 297762) 295 if (!fFirstInputSet) { 296 fFirstInputSet = true; 297 expandAllElementsInViewer(); 298 } 299 } 300 301 @Override viewerInputUpdateComplete(IViewerInputUpdate update)302 protected void viewerInputUpdateComplete(IViewerInputUpdate update) { 303 // handles non-standard debug model 304 IStatus status = update.getStatus(); 305 if ( (status == null || status.isOK()) && update.getElement() != null) { 306 setViewerInput(update.getInputElement()); 307 } else { 308 setViewerInput(new DefaultBreakpointsViewInput(getTreeModelViewer().getPresentationContext())); 309 } 310 } 311 312 313 /** 314 * Returns whether this view is currently tracking the selection from the debug view. 315 * 316 * @return whether this view is currently tracking the debug view's selection 317 */ isTrackingSelection()318 public boolean isTrackingSelection() { 319 final TreeModelViewer viewer = getTreeModelViewer(); 320 if (viewer != null) { 321 return Boolean.TRUE.equals( 322 viewer.getPresentationContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_TRACK_SELECTION) ); 323 } 324 return false; 325 } 326 327 /** 328 * Sets whether this view should track the selection from the debug view. 329 * 330 * @param trackSelection whether or not this view should track the debug view's selection. 331 */ setTrackSelection(boolean trackSelection)332 public void setTrackSelection(boolean trackSelection) { 333 // set the track selection property for non-standard model to track the debug context 334 final TreeModelViewer viewer = getTreeModelViewer(); 335 if (viewer != null) { 336 viewer.getPresentationContext().setProperty( 337 IBreakpointUIConstants.PROP_BREAKPOINTS_TRACK_SELECTION, 338 trackSelection ? Boolean.TRUE : Boolean.FALSE); 339 } 340 } 341 342 /** 343 * Initializes the persisted breakpoints organizers. 344 * @param memento the memento to read 345 */ initBreakpointOrganizers(IMemento memento)346 private void initBreakpointOrganizers(IMemento memento) { 347 if (memento != null) { 348 IMemento node = memento.getChild(IDebugUIConstants.EXTENSION_POINT_BREAKPOINT_ORGANIZERS); 349 if (node == null) { 350 fOrganizers = null; 351 } else { 352 String value = node.getString(KEY_VALUE); 353 if (value != null) { 354 BreakpointOrganizerManager manager = BreakpointOrganizerManager.getDefault(); 355 List<IBreakpointOrganizer> organizers = new ArrayList<>(); 356 for (String id : value.split(",")) { //$NON-NLS-1$ 357 IBreakpointOrganizer organizer = manager.getOrganizer(id); 358 if (organizer != null) { 359 organizers.add(organizer); 360 } 361 } 362 fOrganizers = organizers.toArray(new IBreakpointOrganizer[organizers.size()]); 363 364 for (IBreakpointOrganizer organizer : fOrganizers) { 365 organizer.addPropertyChangeListener(this); 366 } 367 } 368 } 369 } 370 } 371 372 /** 373 * Initializes drag and drop for the breakpoints viewer 374 * @param viewer the viewer to add drag and drop support to 375 */ 376 @Override initDragAndDrop(TreeModelViewer viewer)377 protected void initDragAndDrop(TreeModelViewer viewer) { 378 int ops = DND.DROP_MOVE | DND.DROP_COPY; 379 // drop 380 viewer.addDropSupport(ops, new Transfer[] {LocalSelectionTransfer.getTransfer()}, new BreakpointsDropAdapter(viewer, this)); 381 // Drag 382 viewer.addDragSupport(ops, new Transfer[] {LocalSelectionTransfer.getTransfer()}, new BreakpointsDragAdapter(viewer, this)); 383 } 384 385 @Override saveViewerState(IMemento memento)386 public void saveViewerState(IMemento memento) { 387 StringBuilder buffer = new StringBuilder(); 388 if (fOrganizers != null) { 389 for (int i = 0; i < fOrganizers.length; i++) { 390 IBreakpointOrganizer organizer = fOrganizers[i]; 391 buffer.append(organizer.getIdentifier()); 392 if (i < (fOrganizers.length - 1)) { 393 buffer.append(','); 394 } 395 } 396 IMemento node = memento.createChild(IDebugUIConstants.EXTENSION_POINT_BREAKPOINT_ORGANIZERS); 397 node.putString(KEY_VALUE, buffer.toString()); 398 } 399 super.saveViewerState(memento); 400 } 401 402 /** 403 * Preserves the selection. 404 * 405 * @param selection the selection 406 */ preserveSelection(IStructuredSelection selection)407 public void preserveSelection(IStructuredSelection selection) { 408 if (selection instanceof ITreeSelection && !selection.isEmpty()) { 409 TreePath path = ((ITreeSelection) selection).getPaths()[0]; 410 TreeItem item = (TreeItem) ((TreeModelViewer) getViewer()).findItem(path); 411 Object toselect = null; 412 TreeItem[] siblings = null; 413 if (item != null) { 414 TreeItem parent = item.getParentItem(); 415 if (parent != null) { 416 siblings = parent.getItems(); 417 } else { 418 siblings = item.getParent().getItems(); 419 } 420 if (siblings.length > 1) { 421 for (int i = 0; i < siblings.length; i++) { 422 if (item.equals(siblings[i])) { 423 if (i + 1 >= siblings.length) { 424 toselect = siblings[i - 1].getData(); 425 break; 426 } else { 427 toselect = siblings[i + 1].getData(); 428 break; 429 } 430 431 } 432 } 433 } 434 } 435 if (toselect != null) { 436 getViewer().setSelection(new StructuredSelection(toselect),true); 437 } 438 } 439 } 440 441 /** 442 * Sets the breakpoint organizers for this view. 443 * 444 * @param organizers the organizers, can be <code>null</code>. 445 */ setBreakpointOrganizers(IBreakpointOrganizer[] organizers)446 public void setBreakpointOrganizers(IBreakpointOrganizer[] organizers) { 447 fOrganizers = organizers; 448 449 TreeModelViewer viewer = getTreeModelViewer(); 450 if (viewer != null) { 451 // update the presentation context organizer 452 viewer.getPresentationContext().setProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ORGANIZERS, fOrganizers); 453 } 454 System.out.println(); 455 } 456 457 /** 458 * Sets the breakpoint filter for this view. 459 * @param filter the selection to act as a filter 460 */ setFilterSelection(boolean filter)461 public void setFilterSelection(boolean filter) { 462 TreeModelViewer viewer = getTreeModelViewer(); 463 if (viewer != null) { 464 // update the presentation context filter 465 viewer.getPresentationContext().setProperty( 466 IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION, filter ? Boolean.TRUE : Boolean.FALSE); 467 } 468 } 469 470 @Override breakpointManagerEnablementChanged(boolean enabled)471 public void breakpointManagerEnablementChanged(boolean enabled) { 472 DebugUIPlugin.getStandardDisplay().asyncExec(() -> { 473 IAction action = getAction(ACTION_SKIP_BREAKPOINTS); 474 if (action != null) { 475 ((SkipAllBreakpointsAction) action).updateActionCheckedState(); 476 } 477 }); 478 } 479 480 /** 481 * Expands all elements in the viewer. 482 */ expandAllElementsInViewer()483 public void expandAllElementsInViewer() { 484 Display display = getSite().getShell().getDisplay(); 485 486 final VirtualTreeModelViewer virtualViewer = new VirtualTreeModelViewer( 487 display, 0, ((ITreeModelViewer)getViewer()).getPresentationContext()); 488 489 virtualViewer.setAutoExpandLevel(-1); 490 virtualViewer.addViewerUpdateListener(new IViewerUpdateListener() { 491 @Override 492 public void viewerUpdatesComplete() { 493 ModelDelta stateDelta = new ModelDelta(virtualViewer.getInput(), IModelDelta.NO_CHANGE); 494 virtualViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.EXPAND); 495 ITreeModelViewer treeModelViewer = ((ITreeModelViewer) getViewer()); 496 if (treeModelViewer != null) { 497 ((ITreeModelViewer) getViewer()).updateViewer(stateDelta); 498 } 499 virtualViewer.dispose(); 500 } 501 @Override 502 public void viewerUpdatesBegin() {} 503 @Override 504 public void updateStarted(IViewerUpdate update) {} 505 @Override 506 public void updateComplete(IViewerUpdate update) {} 507 }); 508 virtualViewer.setInput(getViewer().getInput()); 509 } 510 511 512 /** 513 * Returns the breakpoint organizers for this view. 514 * 515 * @return the breakpoint organizers. 516 */ getBreakpointOrganizers()517 public IBreakpointOrganizer[] getBreakpointOrganizers() { 518 return fOrganizers; 519 } 520 521 /** 522 * Returns whether the given selection can be pasted into the given target. 523 * <p> 524 * Scheme: 525 * <ul> 526 * <li>Breakpoints can only be pasted into allowable containers (i..e. like workings sets)</li> 527 * <li>Breakpoints can only be pasted into containers that they do not already reside in</li> 528 * <li>Breakpoints can only be pasted into containers, not other breakpoints</li> 529 * </ul> 530 * </p> 531 * 532 * @param target target of the paste 533 * @param selection the selection to paste 534 * @return whether the given selection can be pasted into the given target 535 * 536 * TODO Remove in favor of using <code>TreeItem</code>s and <code>TreePath</code>s to determine paste targets 537 */ canPaste(Object target, ISelection selection)538 public boolean canPaste(Object target, ISelection selection) { 539 if(!(target instanceof IBreakpointContainer) || !(selection instanceof IStructuredSelection)) { 540 return false; 541 } 542 if(selection == null || selection.isEmpty()) { 543 return false; 544 } 545 IStructuredSelection ss = (IStructuredSelection) selection; 546 IBreakpointContainer container = (IBreakpointContainer) target; 547 for (Iterator<?> iter = ss.iterator(); iter.hasNext();) { 548 IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(iter.next(), IBreakpoint.class); 549 if (breakpoint == null || container.contains(breakpoint) || !container.getOrganizer().canAdd(breakpoint, container.getCategory())) { 550 return false; 551 } 552 } 553 return true; 554 } 555 556 /** 557 * Pastes the selection into the given target 558 * 559 * @param target target of the paste, either a IBreakpointContainer, 560 * or a Breakpoint within a IBreakpointContainer 561 * @param selection breakpoints 562 * @return whether successful 563 * 564 * TODO remove in favor of using <code>TreeItem</code> as paste target 565 */ performPaste(Object target, ISelection selection)566 public boolean performPaste(Object target, ISelection selection) { 567 if (target instanceof IBreakpointContainer && selection instanceof IStructuredSelection) { 568 IBreakpointContainer container = (IBreakpointContainer) target; 569 for (Object object : (IStructuredSelection) selection) { 570 IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(object, IBreakpoint.class); 571 if (breakpoint != null) { 572 container.getOrganizer().addBreakpoint(breakpoint, container.getCategory()); 573 } 574 } 575 return true; 576 } 577 return false; 578 } 579 580 /** 581 * Returns the container from within the specified path that is the container the breakpoint can be removed from 582 * @param path the path to get the container from 583 * @return the first found container that includes the breakpoint that allows removal, or <code>null</code> if none found 584 * @since 3.3 585 */ getRemovableContainer(TreePath path)586 public IBreakpointContainer getRemovableContainer(TreePath path) { 587 if (path != null) { 588 IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(path.getLastSegment(), IBreakpoint.class); 589 if (breakpoint != null) { 590 IBreakpointContainer container = null; 591 for(int i = path.getSegmentCount()-2; i > -1; i--) { 592 Object segment = path.getSegment(i); 593 if (segment instanceof IBreakpointContainer) { 594 container = (IBreakpointContainer) segment; 595 if(container.contains(breakpoint) && 596 container.getOrganizer() != null && 597 container.getOrganizer().canRemove(breakpoint, container.getCategory())) { 598 return container; 599 } 600 } 601 } 602 } 603 } 604 return null; 605 } 606 607 /** 608 * Returns the addable breakpoint container of the specified tree path 609 * @param path the path to get the container for 610 * @return the first found addable container for the specified tree path or <code>null</code> if none found 611 * @since 3.3 612 */ getAddableContainer(TreePath path)613 protected IBreakpointContainer getAddableContainer(TreePath path) { 614 if (path != null) { 615 Object element = path.getLastSegment(); 616 if (element instanceof IBreakpointContainer) { 617 return (IBreakpointContainer)element; 618 } 619 IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(element, IBreakpoint.class); 620 if (breakpoint != null) { 621 IBreakpointContainer container = null; 622 for (int i = path.getSegmentCount()-2; i > -1; i--) { 623 Object segment = path.getSegment(i); 624 if (segment instanceof IBreakpointContainer) { 625 container = (IBreakpointContainer) segment; 626 if (container.contains(breakpoint) && container.getOrganizer().canAdd(breakpoint, container.getCategory())) { 627 return container; 628 } 629 } 630 } 631 } 632 } 633 return null; 634 } 635 /** 636 * This method is used to determine if there is an addable parent container available for the specified drop target. 637 * <p> 638 * A drop target can be either a <code>IBreakpointContainer</code> or an <code>IBreakpoint</code>. This method always checks the entire hierarchy 639 * of the tree path for the specified target in the event one of the parent element does not support dropping. 640 * </p> 641 * @param path the path 642 * @param breakpoint the breakpoint 643 * @return <code>true</code> if there is a parent container available for the drop target <code>false</code> otherwise 644 */ checkAddableParentContainers(TreePath path, IBreakpoint breakpoint)645 private boolean checkAddableParentContainers(TreePath path, IBreakpoint breakpoint) { 646 if (path != null) { 647 Object element = null; 648 for (int i = path.getSegmentCount()-1; i > -1; i--) { 649 element = path.getSegment(i); 650 if (element instanceof IBreakpointContainer) { 651 IBreakpointContainer container = (IBreakpointContainer) element; 652 if (container.contains(breakpoint) || !container.getOrganizer().canAdd(breakpoint, container.getCategory())) { 653 return false; 654 } 655 } 656 } 657 } 658 return true; 659 } 660 661 /** 662 * Returns if the selected item in the tree can be dragged 663 * <p> 664 * Scheme: 665 * <ul> 666 * <li>breakpoint containers cannot be dragged</li> 667 * <li>breakpoints can be dragged iff the container they reside in supports the removal of breakpoints</li> 668 * </ul> 669 * </p> 670 * @param items the tree paths to check if they can be dragged 671 * @return true if the selected element can be dragged, false otherwise 672 * @since 3.3 673 */ canDrag(TreePath[] items)674 boolean canDrag(TreePath[] items) { 675 if(items == null) { 676 return false; 677 } 678 if (items.length == 0) { 679 return false; 680 } 681 for (TreePath item : items) { 682 if (getRemovableContainer(item) == null) { 683 return false; 684 } 685 } 686 return true; 687 } 688 689 /** 690 * Performs the actual removal of breakpoints from their respective (removable) containers on a successful drag operation 691 * @param paths the tree paths to drag 692 * @since 3.3 693 */ performDrag(TreePath[] paths)694 void performDrag(TreePath[] paths) { 695 if (paths == null) { 696 return; 697 } 698 Map<IBreakpointContainer, List<IBreakpoint>> containersToBreakpoints = new HashMap<>(); 699 for (TreePath path : paths) { 700 IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(path.getLastSegment(), IBreakpoint.class); 701 if (breakpoint != null) { 702 IBreakpointContainer container = getRemovableContainer(path); 703 if(container != null) { 704 List<IBreakpoint> list = containersToBreakpoints.get(container); 705 if (list == null) { 706 list = new ArrayList<>(); 707 containersToBreakpoints.put(container, list); 708 } 709 list.add(breakpoint); 710 } 711 } 712 } 713 for (Entry<IBreakpointContainer, List<IBreakpoint>> entry : containersToBreakpoints.entrySet()) { 714 IBreakpointContainer container = entry.getKey(); 715 List<IBreakpoint> list = entry.getValue(); 716 IBreakpointOrganizer organizer = container.getOrganizer(); 717 IBreakpoint[] breakpoints = list.toArray(new IBreakpoint[list.size()]); 718 if (organizer instanceof IBreakpointOrganizerDelegateExtension) { 719 IBreakpointOrganizerDelegateExtension extension = (IBreakpointOrganizerDelegateExtension) organizer; 720 extension.removeBreakpoints(breakpoints, container.getCategory()); 721 } else { 722 for (IBreakpoint breakpoint : breakpoints) { 723 organizer.removeBreakpoint(breakpoint, container.getCategory()); 724 } 725 } 726 } 727 } 728 729 /** 730 * Performs the actual addition of the selected breakpoints to the specified target 731 * @param target the target to add the selection of breakpoints to 732 * @param selection the selection of breakpoints 733 * @return true if the drop occurred, false otherwise 734 * @since 3.3 735 */ performDrop(TreePath target, ITreeSelection selection)736 protected boolean performDrop(TreePath target, ITreeSelection selection) { 737 if(target == null || selection == null) { 738 return false; 739 } 740 IBreakpointContainer container = getAddableContainer(target); 741 if (container == null) { 742 return false; 743 } 744 745 IBreakpointOrganizer organizer = container.getOrganizer(); 746 List<IBreakpoint> breakpoints = new ArrayList<>(selection.size()); 747 for (Iterator<?> iter = selection.iterator(); iter.hasNext();) { 748 IBreakpoint breakpoint = (IBreakpoint) DebugPlugin.getAdapter(iter.next(), IBreakpoint.class); 749 if (breakpoint != null) { 750 breakpoints.add(breakpoint); 751 } 752 } 753 if (organizer instanceof IBreakpointOrganizerDelegateExtension) { 754 IBreakpointOrganizerDelegateExtension extension = (IBreakpointOrganizerDelegateExtension) organizer; 755 extension.addBreakpoints( 756 breakpoints.toArray(new IBreakpoint[breakpoints.size()]), 757 container.getCategory()); 758 } else { 759 for (IBreakpoint breakpoint : breakpoints) { 760 organizer.addBreakpoint(breakpoint, container.getCategory()); 761 } 762 } 763 // TODO expandToLevel(target.getData(), ALL_LEVELS); 764 return true; 765 } 766 767 /** 768 * Determines if the specified element can be dropped into the specified target 769 * <p> 770 * Scheme: 771 * <ul> 772 * <li>Breakpoints can be dropped into working sets</li> 773 * <li>Breakpoints can be dropped into breakpoints, provided there is a drop-able parent of the target breakpoint</li> 774 * </ul> 775 * </p> 776 * @param target the target for the drop 777 * @param selection the selection to see if we can drop 778 * @return true if the specified element can be dropped into the specified target, false otherwise 779 * @since 3.3 780 */ canDrop(TreePath target, ITreeSelection selection)781 boolean canDrop(TreePath target, ITreeSelection selection) { 782 if (selection == null || target == null) { 783 return false; 784 } 785 for (Iterator<?> iter = selection.iterator(); iter.hasNext();) { 786 IBreakpoint breakpoint = (IBreakpoint)DebugPlugin.getAdapter(iter.next(), IBreakpoint.class); 787 if (breakpoint == null || !checkAddableParentContainers(target, breakpoint)){ 788 return false; 789 } 790 } 791 return true; 792 } 793 } 794