From 9ba0a108c20342909431ee83201746e44b80916b Mon Sep 17 00:00:00 2001 From: Patrick Tasse Date: Tue, 9 Jun 2015 15:07:04 -0400 Subject: tmf: Add Copy to Clipboard in TmfEventsTable Change-Id: Ic2ca73787e294ba69fcaa4b111e5a21ff66bb9a1 Signed-off-by: Patrick Tasse Signed-off-by: Marc-Andre Laperle Reviewed-on: https://git.eclipse.org/r/49834 Reviewed-by: Hudson CI --- .../tracecompass/internal/tmf/ui/Messages.java | 5 + .../tmf/ui/commands/CopyToClipboardOperation.java | 163 +++++++++++++++++++++ .../internal/tmf/ui/messages.properties | 5 + .../tmf/ui/viewers/events/TmfEventsTable.java | 65 ++++++-- 4 files changed, 229 insertions(+), 9 deletions(-) create mode 100644 org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/commands/CopyToClipboardOperation.java (limited to 'org.eclipse.tracecompass.tmf.ui/src') diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java index 513534af75..177174cfca 100644 --- a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java @@ -41,6 +41,7 @@ public class Messages extends NLS { public static String TmfEventsTable_ClearFiltersActionText; public static String TmfEventsTable_CollapseFilterMenuName; public static String TmfEventsTable_ContentColumnHeader; + public static String TmfEventsTable_CopyToClipboardActionText; public static String TmfEventsTable_Export_to_text; public static String TmfEventsTable_FilterHint; public static String TmfEventsTable_HideRawActionText; @@ -304,6 +305,10 @@ public class Messages extends NLS { public static String CallStackView_ImportBinaryFileButtonTooltip; public static String CallStackView_ImportBinaryFileDialogTitle; + public static String CopyToClipboardOperation_TaskName; + public static String CopyToClipboardOperation_OutOfMemoryErrorTitle; + public static String CopyToClipboardOperation_OutOfMemoryErrorMessage; + public static String ExportToTextJob_Export_to; public static String ExportToTextJob_Export_trace_to; public static String ExportToTextJob_Unable_to_export_trace; diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/commands/CopyToClipboardOperation.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/commands/CopyToClipboardOperation.java new file mode 100644 index 0000000000..2aca2e7efa --- /dev/null +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/commands/CopyToClipboardOperation.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2015 Ericsson + * + * 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 Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.internal.tmf.ui.commands; + +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tracecompass.internal.tmf.ui.Activator; +import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter; +import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; +import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType; +import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.ui.viewers.events.columns.TmfEventTableColumn; +import org.eclipse.ui.PlatformUI; + +/** + * This operation copies the text of selected trace events to the clipboard. + */ +public class CopyToClipboardOperation implements IRunnableWithProgress { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$ + private final ITmfTrace fTrace; + private final ITmfFilter fFilter; + private final List fColumns; + private final long fStartRank; + private final long fEndRank; + + /** + * Constructor. + * + * @param trace + * the trace to copy events from + * @param filter + * the filter to apply to trace events, or null + * @param columns + * the list of event table columns + * @param start + * the start rank of the selection + * @param end + * the end rank of the selection + */ + public CopyToClipboardOperation(ITmfTrace trace, ITmfFilter filter, List columns, long start, long end) { + fTrace = trace; + fFilter = filter; + fColumns = columns; + fStartRank = start; + fEndRank = end; + } + + @Override + public void run(IProgressMonitor monitor) { + final StringBuilder sb = new StringBuilder(); + monitor.beginTask(Messages.CopyToClipboardOperation_TaskName, (int) (fEndRank - fStartRank + 1)); + + boolean needTab = false; + for (TmfEventTableColumn column : fColumns) { + if (needTab) { + sb.append('\t'); + } + sb.append(column.getHeaderName()); + needTab = true; + } + sb.append(LINE_SEPARATOR); + + copy(sb, monitor); + + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + if (sb.length() == 0) { + return; + } + try { + Clipboard clipboard = new Clipboard(Display.getDefault()); + clipboard.setContents(new Object[] { sb.toString() }, + new Transfer[] { TextTransfer.getInstance() }); + } catch (OutOfMemoryError e) { + sb.setLength(0); + sb.trimToSize(); + showErrorDialog(); + } + } + }); + + monitor.done(); + } + + private IStatus copy(final StringBuilder sb, final IProgressMonitor monitor) { + ITmfEventRequest request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY, fStartRank, (int) (fEndRank - fStartRank + 1), ExecutionType.FOREGROUND) { + @Override + public void handleData(ITmfEvent event) { + super.handleData(event); + if (monitor.isCanceled()) { + cancel(); + return; + } + monitor.worked(1); + if (fFilter == null || fFilter.matches(event)) { + try { + boolean needTab = false; + for (TmfEventTableColumn column : fColumns) { + if (needTab) { + sb.append('\t'); + } + sb.append(column.getItemString(event)); + needTab = true; + } + sb.append(LINE_SEPARATOR); + } catch (OutOfMemoryError e) { + sb.setLength(0); + sb.trimToSize(); + showErrorDialog(); + cancel(); + } + } + } + }; + fTrace.sendRequest(request); + try { + request.waitForCompletion(); + } catch (InterruptedException e) { + Activator.getDefault().logError("Wait for completion interrupted for copy to clipboard ", e); //$NON-NLS-1$ + } + return Status.OK_STATUS; + } + + private static void showErrorDialog() { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + MessageBox confirmOperation = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK); + confirmOperation.setText(Messages.CopyToClipboardOperation_OutOfMemoryErrorTitle); + confirmOperation.setMessage(Messages.CopyToClipboardOperation_OutOfMemoryErrorMessage); + confirmOperation.open(); + } + }); + } +} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties index c1c42847fa..7d91c60fd0 100644 --- a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties @@ -34,6 +34,7 @@ TmfEventsTable_ApplyPresetFilterMenuName=Apply Preset Filter... TmfEventsTable_ClearFiltersActionText=Clear Filters TmfEventsTable_CollapseFilterMenuName=Collapse Events TmfEventsTable_ContentColumnHeader=Content +TmfEventsTable_CopyToClipboardActionText=Copy to Clipboard TmfEventsTable_Export_to_text=Export To Text... TmfEventsTable_FilterHint= TmfEventsTable_HideRawActionText=Hide Raw @@ -303,6 +304,10 @@ CallStackView_ImportBinaryFileButtonText=Import binary file... CallStackView_ImportBinaryFileButtonTooltip=Import a binary file containing debugging symbols CallStackView_ImportBinaryFileDialogTitle=Select Binary File +CopyToClipboardOperation_TaskName=Copying to Clipboard +CopyToClipboardOperation_OutOfMemoryErrorTitle=Out Of Memory Error +CopyToClipboardOperation_OutOfMemoryErrorMessage=The full selection cannot be copied to the clipboard. Press OK to abort. + ExportToTextJob_Export_to=Export to {0}... ExportToTextJob_Export_trace_to=Export trace to {0} ExportToTextJob_Unable_to_export_trace=Unable to export trace to {0} diff --git a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/viewers/events/TmfEventsTable.java b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/viewers/events/TmfEventsTable.java index 56bd95d977..10c10e1642 100644 --- a/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/viewers/events/TmfEventsTable.java +++ b/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/viewers/events/TmfEventsTable.java @@ -21,6 +21,7 @@ package org.eclipse.tracecompass.tmf.ui.viewers.events; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import java.io.FileNotFoundException; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -63,6 +64,7 @@ import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.FontRegistry; import org.eclipse.jface.resource.JFaceResources; @@ -117,6 +119,7 @@ import org.eclipse.tracecompass.common.core.NonNullUtils; import org.eclipse.tracecompass.internal.tmf.core.filter.TmfCollapseFilter; import org.eclipse.tracecompass.internal.tmf.ui.Activator; import org.eclipse.tracecompass.internal.tmf.ui.Messages; +import org.eclipse.tracecompass.internal.tmf.ui.commands.CopyToClipboardOperation; import org.eclipse.tracecompass.internal.tmf.ui.commands.ExportToTextCommandHandler; import org.eclipse.tracecompass.internal.tmf.ui.dialogs.MultiLineInputDialog; import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider; @@ -292,7 +295,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS private ITmfTrace fTrace; private volatile boolean fPackDone = false; private HeaderState fHeaderState = HeaderState.SEARCH; - private long fSelectedRank = 0; + private long fSelectedRank = -1; + private long fSelectedBeginRank = -1; private ITmfTimestamp fSelectedBeginTimestamp = null; private IStatusLineManager fStatusLineManager = null; @@ -507,6 +511,11 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS if (e.item.getData(Key.RANK) instanceof Long) { fSelectedRank = (Long) e.item.getData(Key.RANK); fRawViewer.selectAndReveal((Long) e.item.getData(Key.RANK)); + } else { + fSelectedRank = -1; + } + if (fTable.getSelectionIndices().length == 1) { + fSelectedBeginRank = fSelectedRank; } if (e.item.getData(Key.TIMESTAMP) instanceof ITmfTimestamp) { final ITmfTimestamp ts = NonNullUtils.checkNotNull((ITmfTimestamp) e.item.getData(Key.TIMESTAMP)); @@ -808,6 +817,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS // +1 for header row fTable.setSelection(index + 1); fSelectedRank = rank; + fSelectedBeginRank = fSelectedRank; updateStatusLine(null); final TableItem[] selection = fTable.getSelection(); if ((selection != null) && (selection.length > 0)) { @@ -918,6 +928,37 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS * Create a pop-up menu. */ private void createPopupMenu() { + final IAction copyAction = new Action(Messages.TmfEventsTable_CopyToClipboardActionText) { + @Override + public void run() { + ITmfTrace trace = fTrace; + if (trace == null || (fSelectedRank == -1 && fSelectedBeginRank == -1)) { + return; + } + + List columns = new ArrayList<>(); + for (int i : fTable.getColumnOrder()) { + TableColumn column = fTable.getColumns()[i]; + // Omit the margin column and hidden columns + if (i >= EVENT_COLUMNS_START_INDEX && (column.getResizable() || column.getWidth() > 0)) { + columns.add(fColumns.get(i)); + } + } + + long start = Math.min(fSelectedBeginRank, fSelectedRank); + long end = Math.max(fSelectedBeginRank, fSelectedRank); + final ITmfFilter filter = (ITmfFilter) fTable.getData(Key.FILTER_OBJ); + IRunnableWithProgress operation = new CopyToClipboardOperation(trace, filter, columns, start, end); + try { + PlatformUI.getWorkbench().getProgressService().busyCursorWhile(operation); + } catch (InvocationTargetException e) { + Activator.getDefault().logError("Invocation target exception copying to clipboard ", e); //$NON-NLS-1$ + } catch (InterruptedException e) { + /* ignored */ + } + } + }; + final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) { @Override public void run() { @@ -1088,8 +1129,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS IEvaluationContext context = handlerService.getCurrentState(); List exportColumns = new ArrayList<>(); for (int i : fTable.getColumnOrder()) { - // Omit the margin column - if (i >= EVENT_COLUMNS_START_INDEX) { + TableColumn column = fTable.getColumns()[i]; + // Omit the margin column and hidden columns + if (i >= EVENT_COLUMNS_START_INDEX && (column.getResizable() || column.getWidth() > 0)) { exportColumns.add(fColumns.get(i)); } } @@ -1168,7 +1210,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS tablePopupMenu.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager manager) { - if (fTable.getSelectionIndex() == 0) { + if (fTable.getSelectionIndices().length == 1 && fTable.getSelectionIndices()[0] == 0) { // Right-click on header row if (fHeaderState == HeaderState.FILTER) { tablePopupMenu.add(showSearchBarAction); @@ -1199,6 +1241,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS } // Right-click on table + if (fSelectedRank != -1 && fSelectedBeginRank != -1) { + tablePopupMenu.add(copyAction); + tablePopupMenu.add(new Separator()); + } if (fTable.isVisible() && fRawViewer.isVisible()) { tablePopupMenu.add(hideTableAction); tablePopupMenu.add(hideRawAction); @@ -2250,6 +2296,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS } fTable.setSelection(selection); fSelectedRank = foundRank; + fSelectedBeginRank = fSelectedRank; fRawViewer.selectAndReveal(fSelectedRank); if (foundTimestamp != null) { broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, foundTimestamp)); @@ -2477,13 +2524,14 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS } fTrace = trace; fPackDone = false; - fSelectedRank = 0; fDisposeOnClose = disposeOnClose; // Perform the updates on the UI thread fTable.getDisplay().syncExec(new Runnable() { @Override public void run() { + fSelectedRank = -1; + fSelectedBeginRank = -1; fTable.removeAll(); fCache.setTrace(trace); // Clear the cache if (trace != null) { @@ -2768,6 +2816,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS fPendingGotoRank = rank; } fSelectedRank = rank; + fSelectedBeginRank = fSelectedRank; fTable.setSelection(index + 1); // +1 for header row updateStatusLine(null); } @@ -2874,7 +2923,6 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS final ITmfContext context = fTrace.seekEvent(timestamp); final long rank = context.getRank(); context.dispose(); - fSelectedRank = rank; fTable.getDisplay().asyncExec(new Runnable() { @Override @@ -2884,10 +2932,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS return; } + fSelectedRank = rank; + fSelectedBeginRank = fSelectedRank; int index = (int) rank; - if (fTable.isDisposed()) { - return; - } if (fTable.getData(Key.FILTER_OBJ) != null) { /* +1 for top filter status row */ index = fCache.getFilteredEventIndex(rank) + 1; -- cgit v1.2.3