/******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation 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: * IBM Corporation - initial API and implementation * Wind River Systems - refactored on top of VirtualTreeModelViewer *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.actions.AbstractDebugActionDelegate; import org.eclipse.debug.internal.ui.actions.ActionMessages; import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemValidator; 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.VirtualItem; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem.Index; import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; import org.eclipse.debug.ui.IDebugView; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; public class VirtualCopyToClipboardActionDelegate extends AbstractDebugActionDelegate { private TreeModelViewer fClientViewer; private static final String TAB = "\t"; //$NON-NLS-1$ private static final String SEPARATOR = "line.separator"; //$NON-NLS-1$ /** * Virtual viewer listener. It tracks progress of copy and increments * the progress monitor. */ private class VirtualViewerListener implements ILabelUpdateListener, IVirtualItemListener { VirtualTreeModelViewer fVirtualViewer; IProgressMonitor fProgressMonitor; int fSelectionRootDepth; Set<VirtualItem> fItemsToUpdate; @Override public void labelUpdateStarted(ILabelUpdate update) {} @Override public void labelUpdateComplete(ILabelUpdate update) { VirtualItem updatedItem = fVirtualViewer.findItem(update.getElementPath()); if (fItemsToUpdate.remove(updatedItem)) { incrementProgress(1); } } @Override public void labelUpdatesBegin() { } @Override public void labelUpdatesComplete() { } @Override public void revealed(VirtualItem item) { } @Override public void disposed(VirtualItem item) { if (fItemsToUpdate.remove(item)) { incrementProgress(1); } } private void incrementProgress(int count) { IProgressMonitor pm; synchronized (VirtualCopyToClipboardActionDelegate.this) { pm = fProgressMonitor; } if (pm != null) { pm.worked(count); if (fItemsToUpdate.isEmpty()) { pm.done(); } } } } /** * @see AbstractDebugActionDelegate#initialize(IAction, ISelection) */ @Override protected boolean initialize(IAction action, ISelection selection) { if (!isInitialized()) { IDebugView adapter= getView().getAdapter(IDebugView.class); if (adapter != null) { if (adapter.getViewer() instanceof TreeModelViewer) { setViewer((TreeModelViewer) adapter.getViewer()); } adapter.setAction(getActionId(), action); } return super.initialize(action, selection); } return false; } protected String getActionId() { return IDebugView.COPY_ACTION; } /** * Appends the representation of the specified element (using the label provider and indent) * to the buffer. For elements down to stack frames, children representations * are append to the buffer as well. * @param item Item to append to string * @param buffer String buffer for copy text. * @param indent Current indentation in tree text. */ protected void append(VirtualItem item, StringBuffer buffer, int indent) { for (int i= 0; i < indent; i++) { buffer.append(TAB); } String[] labels = (String[]) item.getData(VirtualItem.LABEL_KEY); if(labels != null && labels.length > 0) { for (int i = 0; i < labels.length; i++) { String text = labels[i]; if(text != null && !text.trim().equals(IInternalDebugCoreConstants.EMPTY_STRING)) { buffer.append(text+TAB); } } buffer.append(System.getProperty(SEPARATOR)); } } private class ItemsToCopyVirtualItemValidator implements IVirtualItemValidator { Set<VirtualItem> fItemsToCopy = Collections.EMPTY_SET; Set<VirtualItem> fItemsToValidate = Collections.EMPTY_SET; @Override public boolean isItemVisible(VirtualItem item) { return fItemsToValidate.contains(item); } @Override public void showItem(VirtualItem item) { } void setItemsToCopy(Set<VirtualItem> itemsToCopy) { fItemsToCopy = itemsToCopy; fItemsToValidate = new HashSet<VirtualItem>(); for (VirtualItem itemToCopy : itemsToCopy) { while (itemToCopy != null) { fItemsToValidate.add(itemToCopy); itemToCopy = itemToCopy.getParent(); } } } } private VirtualTreeModelViewer initVirtualViewer(TreeModelViewer clientViewer, VirtualViewerListener listener, ItemsToCopyVirtualItemValidator validator) { Object input = clientViewer.getInput(); ModelDelta stateDelta = new ModelDelta(input, IModelDelta.NO_CHANGE); clientViewer.saveElementState(TreePath.EMPTY, stateDelta, IModelDelta.EXPAND); VirtualTreeModelViewer virtualViewer = new VirtualTreeModelViewer( clientViewer.getDisplay(), SWT.VIRTUAL, clientViewer.getPresentationContext(), validator); virtualViewer.setFilters(clientViewer.getFilters()); virtualViewer.addLabelUpdateListener(listener); virtualViewer.getTree().addItemListener(listener); String[] columns = clientViewer.getPresentationContext().getColumns(); virtualViewer.setInput(input); if (virtualViewer.canToggleColumns()) { virtualViewer.setShowColumns(clientViewer.isShowColumns()); virtualViewer.setVisibleColumns(columns); } virtualViewer.updateViewer(stateDelta); // Parse selected items from client viewer and add them to the virtual viewer selection. listener.fSelectionRootDepth = Integer.MAX_VALUE; TreeItem[] selection = getSelectedItems(clientViewer); Set<VirtualItem> vSelection = new HashSet<VirtualItem>(selection.length * 4 / 3); for (int i = 0; i < selection.length; i++) { TreePath parentPath = fClientViewer.getTreePathFromItem(selection[i].getParentItem()); listener.fSelectionRootDepth = Math.min(parentPath.getSegmentCount() + 1, listener.fSelectionRootDepth); VirtualItem parentVItem = virtualViewer.findItem(parentPath); if (parentVItem != null) { int index = -1; TreeItem parentItem = selection[i].getParentItem(); if (parentItem != null) { index = parentItem.indexOf(selection[i]); } else { Tree parentTree = selection[i].getParent(); index = parentTree.indexOf(selection[i]); } index = ((ITreeModelContentProvider)clientViewer.getContentProvider()).viewToModelIndex(parentPath, index); vSelection.add( parentVItem.getItem(new Index(index)) ); } } validator.setItemsToCopy(vSelection); listener.fItemsToUpdate = new HashSet<VirtualItem>(vSelection); virtualViewer.getTree().validate(); return virtualViewer; } protected TreeItem[] getSelectedItems(TreeModelViewer clientViewer) { return clientViewer.getTree().getSelection(); } /** * Do the specific action using the current selection. * @param action Action that is running. */ @Override public void run(final IAction action) { if (fClientViewer.getSelection().isEmpty()) { return; } final VirtualViewerListener listener = new VirtualViewerListener(); ItemsToCopyVirtualItemValidator validator = new ItemsToCopyVirtualItemValidator(); VirtualTreeModelViewer virtualViewer = initVirtualViewer(fClientViewer, listener, validator); listener.fVirtualViewer = virtualViewer; ProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(fClientViewer.getControl().getShell(), 500); final IProgressMonitor monitor = dialog.getProgressMonitor(); dialog.setCancelable(true); IRunnableWithProgress runnable = new IRunnableWithProgress() { @Override public void run(final IProgressMonitor m) throws InvocationTargetException, InterruptedException { synchronized(listener) { listener.fProgressMonitor = m; listener.fProgressMonitor.beginTask(DebugUIPlugin.removeAccelerators(getAction().getText()), listener.fItemsToUpdate.size()); } while (!listener.fItemsToUpdate.isEmpty() && !listener.fProgressMonitor.isCanceled()) { Thread.sleep(1); } synchronized(listener) { listener.fProgressMonitor = null; } } }; try { dialog.run(true, true, runnable); } catch (InvocationTargetException e) { DebugUIPlugin.log(e); return; } catch (InterruptedException e) { return; } if (!monitor.isCanceled()) { copySelectionToClipboard(virtualViewer, validator.fItemsToCopy, listener.fSelectionRootDepth); } virtualViewer.removeLabelUpdateListener(listener); virtualViewer.getTree().removeItemListener(listener); virtualViewer.dispose(); } private void copySelectionToClipboard(VirtualTreeModelViewer virtualViewer, Set<VirtualItem> itemsToCopy, int selectionRootDepth) { StringBuffer buffer = new StringBuffer(); writeItemToBuffer (virtualViewer.getTree(), itemsToCopy, buffer, -selectionRootDepth); writeBufferToClipboard(buffer); } protected void writeItemToBuffer(VirtualItem item, Set<VirtualItem> itemsToCopy, StringBuffer buffer, int indent) { if (itemsToCopy.contains(item)) { append(item, buffer, indent); } VirtualItem[] children = item.getItems(); if (children != null) { for (int i = 0; i < children.length; i++) { writeItemToBuffer(children[i], itemsToCopy, buffer, indent + 1); } } } protected void writeBufferToClipboard(StringBuffer buffer) { if (buffer.length() == 0) { return; } TextTransfer plainTextTransfer = TextTransfer.getInstance(); Clipboard clipboard= new Clipboard(fClientViewer.getControl().getDisplay()); try { clipboard.setContents( new String[]{buffer.toString()}, new Transfer[]{plainTextTransfer}); } catch (SWTError e){ if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) { throw e; } if (MessageDialog.openQuestion(fClientViewer.getControl().getShell(), ActionMessages.CopyToClipboardActionDelegate_Problem_Copying_to_Clipboard_1, ActionMessages.CopyToClipboardActionDelegate_There_was_a_problem_when_accessing_the_system_clipboard__Retry__2)) { // writeBufferToClipboard(buffer); } } finally { clipboard.dispose(); } } protected TreeModelViewer getViewer() { return fClientViewer; } protected void setViewer(TreeModelViewer viewer) { fClientViewer = viewer; } /** * @see AbstractDebugActionDelegate#doAction(Object) */ @Override protected void doAction(Object element) { //not used } @Override protected boolean getEnableStateForSelection(IStructuredSelection selection) { if (selection.isEmpty()) { return true; } else { return super.getEnableStateForSelection(selection); } } @Override public void runWithEvent(IAction action, Event event) { run(action); } }