tmf: Add Copy to Clipboard in TmfEventsTable
authorPatrick Tasse <patrick.tasse@gmail.com>
Tue, 9 Jun 2015 19:07:04 +0000 (15:07 -0400)
committerPatrick Tasse <patrick.tasse@gmail.com>
Thu, 11 Jun 2015 19:47:46 +0000 (15:47 -0400)
Change-Id: Ic2ca73787e294ba69fcaa4b111e5a21ff66bb9a1
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/49834
Reviewed-by: Hudson CI
org.eclipse.tracecompass.tmf.ui/plugin.properties
org.eclipse.tracecompass.tmf.ui/plugin.xml
org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/Messages.java
org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/commands/CopyToClipboardOperation.java [new file with mode: 0644]
org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/messages.properties
org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/viewers/events/TmfEventsTable.java

index cddc1fdd373fcb7bd6c75fe6678d29ef3f7aeaea..4105a52c73390f7e84b0ed29a5ffe4c609e6a278 100644 (file)
@@ -113,7 +113,10 @@ command.select_trace_type.bundle = Bundle
 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
index a274ed0f4cab5fbfad7f2663da67f6dd0bd485b4..410285eac6bdb598fa2f65e394dde87b6efe351b 100644 (file)
             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"
index 513534af750548e21d5fcd9c7cd615321559588e..177174cfca6ab667e5383841f9c651f001bb7bda 100644 (file)
@@ -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 (file)
index 0000000..2aca2e7
--- /dev/null
@@ -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<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();
+            }
+        });
+    }
+}
index c1c42847fa0143a4dfe6f33d46f7b9e98263b2bb..7d91c60fd002be62b8d9e41a948e54c0c7d02fb9 100644 (file)
@@ -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=<filter>
 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}
index 56bd95d977b211a3a6218d1552ecb2167619e5cf..10c10e164237a1dbf040039a69d777ee01ea09c5 100644 (file)
@@ -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<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() {
@@ -1088,8 +1129,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     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));
                         }
                     }
@@ -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;
This page took 0.039638 seconds and 5 git commands to generate.