tmf: support collapse filter in events table
authorBernd Hufmann <Bernd.Hufmann@ericsson.com>
Mon, 25 Aug 2014 18:22:57 +0000 (14:22 -0400)
committerBernd Hufmann <bernd.hufmann@ericsson.com>
Tue, 30 Sep 2014 13:30:12 +0000 (09:30 -0400)
Change-Id: Iaa87955affa8004764a92e078c51cd854850f44a
Signed-off-by: Bernd Hufmann <Bernd.Hufmann@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/32350
Tested-by: Hudson CI
Reviewed-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Tested-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
org.eclipse.linuxtools.tmf.core/META-INF/MANIFEST.MF
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/internal/tmf/ui/Messages.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/internal/tmf/ui/messages.properties
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/events/TmfEventsCache.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/events/TmfEventsTable.java

index 6776e89c0ff278692244182937e0797f3f5ee0a4..de0d666f6d78c69b2548c2a760a34671dc10215b 100644 (file)
@@ -14,7 +14,7 @@ Require-Bundle: org.eclipse.core.runtime,
 Export-Package: org.eclipse.linuxtools.internal.tmf.core;x-friends:="org.eclipse.linuxtools.tmf.core.tests",
  org.eclipse.linuxtools.internal.tmf.core.analysis;x-friends:="org.eclipse.linuxtools.tmf.core.tests",
  org.eclipse.linuxtools.internal.tmf.core.component;x-friends:="org.eclipse.linuxtools.tmf.core.tests",
- org.eclipse.linuxtools.internal.tmf.core.filter;x-friends:="org.eclipse.linuxtools.tmf.core.tests",
+ org.eclipse.linuxtools.internal.tmf.core.filter;x-friends:="org.eclipse.linuxtools.tmf.core.tests,org.eclipse.linuxtools.tmf.ui",
  org.eclipse.linuxtools.internal.tmf.core.request;x-friends:="org.eclipse.linuxtools.tmf.core.tests",
  org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.partial;x-friends:="org.eclipse.linuxtools.statesystem.core.tests",
  org.eclipse.linuxtools.internal.tmf.core.statesystem.mipmap;x-friends:="org.eclipse.linuxtools.tmf.core.tests",
index 17a6591dacf1b14cf253cea76348b5ceeb3924be..e82aaa8502f5b3aaf8bd5920e08d095c05c9598a 100644 (file)
@@ -39,6 +39,7 @@ public class Messages extends NLS {
     public static String TmfEventsTable_AddBookmarkDialogTitle;
     public static String TmfEventsTable_ApplyPresetFilterMenuName;
     public static String TmfEventsTable_ClearFiltersActionText;
+    public static String TmfEventsTable_CollapseFilterMenuName;
     public static String TmfEventsTable_ContentColumnHeader;
     public static String TmfEventsTable_Export_to_text;
     public static String TmfEventsTable_FilterHint;
index 07789b254d1d3a505106610de7f4e913e2878e7f..a466259bdf3174f95021aa383e679ae35a806d76 100644 (file)
@@ -32,6 +32,7 @@ TmfEventsTable_AddBookmarkDialogMessage=Enter Bookmark Description:
 TmfEventsTable_AddBookmarkDialogTitle=Add Bookmark
 TmfEventsTable_ApplyPresetFilterMenuName=Apply Preset Filter...
 TmfEventsTable_ClearFiltersActionText=Clear Filters
+TmfEventsTable_CollapseFilterMenuName=Collapse Events
 TmfEventsTable_ContentColumnHeader=Content
 TmfEventsTable_Export_to_text=Export To Text...
 TmfEventsTable_FilterHint=<filter>
index d54ecc1d63c88b153fa6b9cd489ac04c6257afe4..015d2e9ecbdfc449ddaf1234dec3bbfcd4839b35 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2013 Ericsson
+ * Copyright (c) 2011, 2014 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -8,6 +8,7 @@
  *
  * Contributors:
  *   Patrick Tasse - Initial API and implementation
+ *   Bernd Hufmann - Add support for event collapsing
  ******************************************************************************/
 
 package org.eclipse.linuxtools.tmf.ui.viewers.events;
@@ -20,12 +21,16 @@ import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.linuxtools.internal.tmf.core.filter.TmfCollapseFilter;
 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
 import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
+import org.eclipse.linuxtools.tmf.core.event.ITmfEventType;
 import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;
 import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
 import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
 
@@ -35,20 +40,34 @@ import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
  * This can help avoid re-reading the trace when the user scrolls a window,
  * for example.
  *
- * @version 1.0
  * @author Patrick Tasse
  */
 public class TmfEventsCache {
 
     /**
-     * The generic TMF Events table cached event
+     * The generic TMF Events table cached event.
      *
-     * @version 1.0
      * @author Patrick Tasse
      */
-    public static class CachedEvent {
+    public static class CachedEvent implements ITmfEvent {
+        /**
+         * Event reference.
+         *
+         * When {@link TmfCollapseFilter} is active then it's event reference
+         * of the first event of repeated events.
+         */
         ITmfEvent event;
+        /**
+         * Events rank.
+         *
+         * When {@link TmfCollapseFilter} is active then it's event rank of the
+         * first event of repeated events.
+         */
         long rank;
+        /**
+         * Number times event is repeated. Updated by using {@link TmfCollapseFilter}
+         */
+        long repeatCount;
 
         /**
          * Constructor for new cached events.
@@ -62,6 +81,62 @@ public class TmfEventsCache {
             this.event = iTmfEvent;
             this.rank = rank;
         }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public Object getAdapter(Class adapter) {
+            return event.getAdapter(adapter);
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public ITmfTrace getTrace() {
+            return event.getTrace();
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public long getRank() {
+            return event.getRank();
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public ITmfTimestamp getTimestamp() {
+            return event.getTimestamp();
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public String getSource() {
+            return event.getSource();
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public ITmfEventType getType() {
+            return event.getType();
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public ITmfEventField getContent() {
+            return event.getContent();
+        }
+        /**
+         * @since 3.1
+         */
+        @Override
+        public String getReference() {
+            return event.getReference();
+        }
     }
 
     private final CachedEvent[] fCache;
@@ -190,6 +265,21 @@ public class TmfEventsCache {
         }
     }
 
+    /**
+     * Update event repeat count at index
+     *
+     * @param index
+     *            The index this event occupies in the cache
+     *
+     * @since 3.1
+     */
+    public synchronized void updateCollapsedEvent(int index) {
+        int i = index - fCacheStartIndex;
+        if (i < fCache.length) {
+            fCache[i].repeatCount++;
+        }
+    }
+
     /**
      * Get the cache index of an event from his rank in the trace. This will
      * take in consideration any filter that might be applied.
@@ -361,6 +451,10 @@ public class TmfEventsCache {
                             if (fFilter != null) {
                                 fTable.cacheUpdated(false);
                             }
+                        } else if (((fFilter != null) && !fFilter.matches(event)) && (skipCount <= 0)) { // TODO fix duplicated call to matches()
+                            if ((count > 0) && (fFilter instanceof TmfCollapseFilter)) {
+                                fCache[count - 1].repeatCount++;
+                            }
                         }
                         if (count >= fCache.length) {
                             cancel();
index 8a950c16e6a535f9269096fdf738f758451d5a82..22e867564a7128baa4c763e00774c83036aec33b 100644 (file)
@@ -72,6 +72,7 @@ import org.eclipse.jface.viewers.LabelProvider;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.window.Window;
+import org.eclipse.linuxtools.internal.tmf.core.filter.TmfCollapseFilter;
 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
 import org.eclipse.linuxtools.internal.tmf.ui.Messages;
 import org.eclipse.linuxtools.internal.tmf.ui.commands.ExportToTextCommandHandler;
@@ -79,6 +80,7 @@ import org.eclipse.linuxtools.internal.tmf.ui.dialogs.MultiLineInputDialog;
 import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
 import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.event.collapse.ITmfCollapsibleEvent;
 import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfCallsite;
 import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfModelLookup;
 import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfSourceLookup;
@@ -99,6 +101,7 @@ import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
 import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
 import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
 import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
 import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableColumn;
@@ -176,6 +179,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     protected static final @NonNull String EMPTY_STRING = ""; //$NON-NLS-1$
 
+    private static final boolean IS_LINUX = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
+
     private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
             "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
     private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$
@@ -190,6 +195,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;
     private static final int MAX_CACHE_SIZE = 1000;
 
+    private static final int MARGIN_COLUMN_INDEX = 0;
+    private static final int FILTER_SUMMARY_INDEX = 1;
+    private static final int EVENT_COLUMNS_START_INDEX = MARGIN_COLUMN_INDEX + 1;
+
     /**
      * Default set of columns to use for trace types that do not specify
      * anything
@@ -265,7 +274,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private SashForm fSashForm;
     private TmfRawEventViewer fRawViewer;
     private ITmfTrace fTrace;
-    private boolean fPackDone = false;
+    volatile private boolean fPackDone = false;
     private HeaderState fHeaderState = HeaderState.SEARCH;
     private long fSelectedRank = 0;
     private ITmfTimestamp fSelectedBeginTimestamp = null;
@@ -408,6 +417,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fColumns.addAll(columns);
         }
 
+        TmfMarginColumn collapseCol = new TmfMarginColumn();
+        fColumns.add(MARGIN_COLUMN_INDEX, collapseCol);
+
         // Create the UI columns in the table
         for (TmfEventTableColumn col : fColumns) {
             TableColumn column = fTable.newTableColumn(SWT.LEFT);
@@ -415,6 +427,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             column.setToolTipText(col.getHeaderTooltip());
             column.setData(Key.FIELD_ID, col.getFilterFieldId());
             column.pack();
+            if (col instanceof TmfMarginColumn) {
+                column.setResizable(false);
+            }
         }
 
         // Set the frozen row for header row
@@ -494,7 +509,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
                 final CachedEvent cachedEvent = fCache.getEvent(index);
                 if (cachedEvent != null) {
-                    setItemData(item, cachedEvent.event, cachedEvent.rank);
+                    setItemData(item, cachedEvent, cachedEvent.rank);
                     return;
                 }
 
@@ -554,7 +569,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         layout.marginWidth = 2;
                         tooltipShell.setLayout(layout);
                         final Label label = new Label(tooltipShell, SWT.WRAP);
-                        String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : ""); //$NON-NLS-1$ //$NON-NLS-2$
+                        String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : EMPTY_STRING); //$NON-NLS-1$
                         label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
                         label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
                         label.setText(text);
@@ -567,7 +582,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                          * the method toDisplay() expects coordinates relative to an origin that includes the table header.
                          */
                         int y = event.y;
-                        if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
+                        if (IS_LINUX) {
                             y += fTable.getHeaderHeight();
                         }
                         Point pt = fTable.toDisplay(event.x, y);
@@ -710,7 +725,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     IMarker marker = null;
                     try {
                         String fileName = cs.getFileName();
-                        final String trimmedPath = fileName.replaceAll("\\.\\./", ""); //$NON-NLS-1$ //$NON-NLS-2$
+                        final String trimmedPath = fileName.replaceAll("\\.\\./", EMPTY_STRING); //$NON-NLS-1$
                         final ArrayList<IFile> files = new ArrayList<>();
                         ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
                             @Override
@@ -819,9 +834,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 try {
                     HashMap<String, Object> parameters = new HashMap<>();
                     Command command = cmdService.getCommand(ExportToTextCommandHandler.COMMAND_ID);
-                    ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command,parameters);
+                    ParameterizedCommand cmd = ParameterizedCommand.generateCommand(command, parameters);
+
                     IEvaluationContext context = handlerService.getCurrentState();
-                    context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, fColumns);
+                    // Omit the margin column
+                    List<TmfEventTableColumn> exportColumns = fColumns.subList(EVENT_COLUMNS_START_INDEX, fColumns.size());
+                    context.addVariable(ExportToTextCommandHandler.TMF_EVENT_TABLE_COLUMNS_ID, exportColumns);
+
                     handlerService.executeCommandInContext(cmd, null, context);
                 } catch (ExecutionException e) {
                     displayException(e);
@@ -858,6 +877,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             }
         };
 
+        final IAction collapseAction = new Action(Messages.TmfEventsTable_CollapseFilterMenuName) {
+            @Override
+            public void run() {
+                applyFilter(new TmfCollapseFilter());
+            }
+        };
+
         class ToggleBookmarkAction extends Action {
             Long fRank;
 
@@ -943,6 +969,24 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     }
                 }
 
+                // only show collapse filter if at least one trace can be collapsed
+                boolean isCollapsible = false;
+                if (fTrace != null) {
+                    ITmfTrace traces[] = TmfTraceManager.getTraceSet(fTrace);
+                    for (ITmfTrace trace : traces) {
+                        Class <? extends ITmfEvent> eventClass = trace.getEventType();
+                        isCollapsible = ITmfCollapsibleEvent.class.isAssignableFrom(eventClass);
+                        if (isCollapsible) {
+                            break;
+                        }
+                    }
+                }
+
+                if (isCollapsible && !(fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter)) {
+                    tablePopupMenu.add(collapseAction);
+                    tablePopupMenu.add(new Separator());
+                }
+
                 tablePopupMenu.add(clearFiltersAction);
                 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
                 if (savedFilters.length > 0) {
@@ -1066,7 +1110,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            Which rank this event has in the trace/experiment
      */
     protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
-        item.setText(getItemStrings(fColumns, event));
+        String[] itemStrings = getItemStrings(fColumns, event);
+        item.setText(itemStrings);
         item.setData(event);
         item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
         item.setData(Key.RANK, rank);
@@ -1122,6 +1167,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         } else {
             item.setImage((Image) null);
         }
+
+        if ((itemStrings[MARGIN_COLUMN_INDEX] != null) && !itemStrings[MARGIN_COLUMN_INDEX].isEmpty()) {
+            packMarginColumn();
+        }
     }
 
     /**
@@ -1140,7 +1189,8 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             txtKey = Key.FILTER_TXT;
         }
         item.setForeground(fGrayColor);
-        for (int i = 0; i < fTable.getColumns().length; i++) {
+        // Ignore collapse and image column
+        for (int i = EVENT_COLUMNS_START_INDEX; i < fTable.getColumns().length; i++) {
             final TableColumn column = fTable.getColumns()[i];
             final String filter = (String) column.getData(txtKey);
             if (filter == null) {
@@ -1167,15 +1217,18 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     protected void setFilterStatusRowItemData(final TableItem item) {
         for (int i = 0; i < fTable.getColumns().length; i++) {
-            if (i == 0) {
+            if (i == MARGIN_COLUMN_INDEX) {
                 if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
                     item.setImage(FILTER_IMAGE);
                 } else {
                     item.setImage(STOP_IMAGE);
                 }
-                item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
+            }
+
+            if (i == FILTER_SUMMARY_INDEX) {
+                item.setText(FILTER_SUMMARY_INDEX, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
             } else {
-                item.setText(i, ""); //$NON-NLS-1$
+                item.setText(i, EMPTY_STRING);
             }
         }
         item.setData(null);
@@ -1503,6 +1556,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
         fireFilterApplied(null);
         updateStatusLine(null);
+
+        // Set original width
+        fTable.getColumns()[MARGIN_COLUMN_INDEX].setWidth(0);
+        packMarginColumn();
     }
 
     /**
@@ -1543,13 +1600,20 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     if (request.isCancelled()) {
                         return;
                     }
+                    boolean refresh = false;
                     if (filter.matches(event)) {
                         final long rank = fFilterCheckCount;
                         final int index = (int) fFilterMatchCount;
                         fFilterMatchCount++;
                         fCache.storeEvent(event, rank, index);
-                        refreshTable();
-                    } else if ((fFilterCheckCount % 100) == 0) {
+                        refresh = true;
+                    } else {
+                        if (filter instanceof TmfCollapseFilter) {
+                            fCache.updateCollapsedEvent((int) fFilterMatchCount - 1);
+                        }
+                    }
+
+                    if (refresh || (fFilterCheckCount % 100) == 0) {
                         refreshTable();
                     }
                     fFilterCheckCount++;
@@ -1888,28 +1952,41 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             return;
         }
         fTable.setRedraw(false);
+        try {
+            TableColumn tableColumns[] = fTable.getColumns();
+            for (int i = 0; i < tableColumns.length; i++) {
+                final TableColumn column = tableColumns[i];
+                packSingleColumn(i, column);
+            }
+        } finally {
+            // Make sure that redraw is always enabled.
+            fTable.setRedraw(true);
+        }
+        fPackDone = true;
+    }
 
-        boolean isLinux = System.getProperty("os.name").contains("Linux") ? true : false; //$NON-NLS-1$ //$NON-NLS-2$
 
-        TableColumn tableColumns[] = fTable.getColumns();
-        for (int i = 0; i < tableColumns.length; i++) {
-            final TableColumn column = tableColumns[i];
-            final int headerWidth = column.getWidth();
-            column.pack();
-            // Workaround for Linux which doesn't consider the image width of
-            // search/filter row in TableColumn.pack() after having executed
-            // TableItem.setImage((Image)null) for other rows than search/filter row.
-            if (isLinux && (i == 0)) {
-                column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
-            }
+    private void packMarginColumn() {
+        TableColumn[] columns = fTable.getColumns();
+        if (columns.length > 0) {
+            packSingleColumn(0, columns[0]);
+        }
+    }
 
-            if (column.getWidth() < headerWidth) {
-                column.setWidth(headerWidth);
-            }
+    private void packSingleColumn(int i, final TableColumn column) {
+        final int headerWidth = column.getWidth();
+        column.pack();
+        // Workaround for Linux which doesn't consider the image width of
+        // search/filter row in TableColumn.pack() after having executed
+        // TableItem.setImage((Image)null) for other rows than search/filter row.
+        boolean isCollapseFilter = fTable.getData(Key.FILTER_OBJ) instanceof TmfCollapseFilter;
+        if (IS_LINUX && (i == 0) && isCollapseFilter) {
+            column.setWidth(column.getWidth() + SEARCH_IMAGE.getBounds().width);
         }
 
-        fTable.setRedraw(true);
-        fPackDone = true;
+        if (column.getWidth() < headerWidth) {
+            column.setWidth(headerWidth);
+        }
     }
 
     /**
@@ -1931,7 +2008,18 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         synchronized (columns) {
             List<String> itemStrings = new ArrayList<>(columns.size());
             for (TmfEventTableColumn column : columns) {
-                itemStrings.add(column.getItemString(event));
+                ITmfEvent passedEvent = event;
+                if (!(column instanceof TmfMarginColumn) && (event instanceof CachedEvent)) {
+                    // Make sure that the event object from the trace is passed
+                    // to all columns but the TmfMarginColumn
+                    passedEvent = ((CachedEvent) event).event;
+                }
+                if (passedEvent == null) {
+                    itemStrings.add(EMPTY_STRING);
+                } else {
+                    itemStrings.add(column.getItemString(passedEvent));
+                }
+
             }
             return itemStrings.toArray(new String[0]);
         }
@@ -2009,7 +2097,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      */
     public void setStatusLineManager(IStatusLineManager statusLineManager) {
         if (fStatusLineManager != null && statusLineManager == null) {
-            fStatusLineManager.setMessage(""); //$NON-NLS-1$
+            fStatusLineManager.setMessage(EMPTY_STRING);
         }
         fStatusLineManager = statusLineManager;
     }
@@ -2417,4 +2505,33 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.refresh();
         fTable.redraw();
     }
+
+    /**
+    * Margin column for images and special text (e.g. collapse count)
+    */
+   private static final class TmfMarginColumn extends TmfEventTableColumn {
+
+       private static final @NonNull String HEADER = EMPTY_STRING;
+
+       /**
+        * Constructor
+        */
+       public TmfMarginColumn() {
+           super(HEADER);
+       }
+
+       @Override
+       public String getItemString(ITmfEvent event) {
+           if (!(event instanceof CachedEvent) || ((CachedEvent) event).repeatCount == 0) {
+               return EMPTY_STRING;
+           }
+           return "+" + ((CachedEvent) event).repeatCount; //$NON-NLS-1$
+       }
+
+       @Override
+       public String getFilterFieldId() {
+           return null;
+       }
+   }
+
 }
This page took 0.035656 seconds and 5 git commands to generate.