command.select_trace_type.type = Trace Type
command.select_trace_type.icon = Icon
-command.export_to_text = Export To Text...
+command.copy_to_clipboard = Copy to Clipboard
+command.copy_to_clipboard.description = Copy to Clipboard
+
+command.export_to_text = Export to Text...
command.export_to_text.description = Export trace to text...
command.synchronize_traces = Synchronize Traces
id="org.eclipse.linuxtools.tmf.ui.import"
name="%command.import">
</command>
+ <command
+ categoryId="org.eclipse.linuxtools.tmf.ui.commands.category"
+ description="%command.copy_to_clipboard.description"
+ id="org.eclipse.tracecompass.tmf.ui.copy_to_clipboard"
+ name="%command.copy_to_clipboard">
+ </command>
<command
categoryId="org.eclipse.linuxtools.tmf.ui.commands.category"
description="%command.export_to_text.description"
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;
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;
--- /dev/null
+/*******************************************************************************
+ * 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<TmfEventTableColumn> 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<TmfEventTableColumn> 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();
+ }
+ });
+ }
+}
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=<filter>
TmfEventsTable_HideRawActionText=Hide Raw
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}
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;
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;
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;
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;
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));
// +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)) {
* 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<TmfEventTableColumn> 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() {
IEvaluationContext context = handlerService.getCurrentState();
List<TmfEventTableColumn> 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));
}
}
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);
}
// 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);
}
fTable.setSelection(selection);
fSelectedRank = foundRank;
+ fSelectedBeginRank = fSelectedRank;
fRawViewer.selectAndReveal(fSelectedRank);
if (foundTimestamp != null) {
broadcast(new TmfSelectionRangeUpdatedSignal(TmfEventsTable.this, foundTimestamp));
}
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) {
fPendingGotoRank = rank;
}
fSelectedRank = rank;
+ fSelectedBeginRank = fSelectedRank;
fTable.setSelection(index + 1); // +1 for header row
updateStatusLine(null);
}
final ITmfContext context = fTrace.seekEvent(timestamp);
final long rank = context.getRank();
context.dispose();
- fSelectedRank = rank;
fTable.getDisplay().asyncExec(new Runnable() {
@Override
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;