Fix time range synchronization issues
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / events / TmfEventsTable.java
CommitLineData
db4721fb
PT
1/*******************************************************************************
2 * Copyright (c) 2010, 2011, 2012 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 * Patrick Tasse - Factored out from events view
12 * Francois Chouinard - Replaced Table by TmfVirtualTable
13 * Patrick Tasse - Filter implementation (inspired by www.eclipse.org/mat)
14 *******************************************************************************/
15
16package org.eclipse.linuxtools.tmf.ui.viewers.events;
17
18import java.util.ArrayList;
19import java.util.Arrays;
20import java.util.HashMap;
21import java.util.List;
22import java.util.Map;
23import java.util.Map.Entry;
24import java.util.regex.Pattern;
25import java.util.regex.PatternSyntaxException;
26
27import org.eclipse.core.resources.IFile;
28import org.eclipse.core.resources.IMarker;
29import org.eclipse.core.resources.IResource;
30import org.eclipse.core.runtime.CoreException;
31import org.eclipse.core.runtime.IProgressMonitor;
32import org.eclipse.core.runtime.IStatus;
93bfd50a 33import org.eclipse.core.runtime.ListenerList;
db4721fb
PT
34import org.eclipse.core.runtime.Status;
35import org.eclipse.core.runtime.jobs.Job;
36import org.eclipse.jface.action.Action;
37import org.eclipse.jface.action.IAction;
38import org.eclipse.jface.action.IMenuListener;
39import org.eclipse.jface.action.IMenuManager;
40import org.eclipse.jface.action.MenuManager;
41import org.eclipse.jface.action.Separator;
42import org.eclipse.jface.dialogs.InputDialog;
43import org.eclipse.jface.dialogs.MessageDialog;
44import org.eclipse.jface.resource.FontDescriptor;
45import org.eclipse.jface.resource.JFaceResources;
46import org.eclipse.jface.resource.LocalResourceManager;
93bfd50a
PT
47import org.eclipse.jface.util.SafeRunnable;
48import org.eclipse.jface.viewers.ISelection;
49import org.eclipse.jface.viewers.ISelectionChangedListener;
50import org.eclipse.jface.viewers.ISelectionProvider;
93bfd50a
PT
51import org.eclipse.jface.viewers.SelectionChangedEvent;
52import org.eclipse.jface.viewers.StructuredSelection;
db4721fb
PT
53import org.eclipse.jface.window.Window;
54import org.eclipse.linuxtools.internal.tmf.ui.Activator;
55import org.eclipse.linuxtools.internal.tmf.ui.Messages;
3be2946f 56import org.eclipse.linuxtools.internal.tmf.ui.dialogs.MultiLineInputDialog;
db4721fb
PT
57import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;
58import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
59import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
60import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
61import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
62import org.eclipse.linuxtools.tmf.core.event.TmfEventField;
63import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;
64import org.eclipse.linuxtools.tmf.core.filter.ITmfFilter;
65import org.eclipse.linuxtools.tmf.core.filter.model.ITmfFilterTreeNode;
66import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterAndNode;
67import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterMatchesNode;
68import org.eclipse.linuxtools.tmf.core.filter.model.TmfFilterNode;
69import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;
70import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
faa38350
PT
71import org.eclipse.linuxtools.tmf.core.signal.TmfEventFilterAppliedSignal;
72import org.eclipse.linuxtools.tmf.core.signal.TmfEventSearchAppliedSignal;
db4721fb
PT
73import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
74import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
75import org.eclipse.linuxtools.tmf.core.signal.TmfTraceUpdatedSignal;
76import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
77import org.eclipse.linuxtools.tmf.core.trace.ITmfLocation;
78import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
79import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
80import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
81import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
82import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;
83import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;
84import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
85import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;
86import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
87import org.eclipse.swt.SWT;
88import org.eclipse.swt.custom.SashForm;
89import org.eclipse.swt.custom.TableEditor;
90import org.eclipse.swt.events.FocusAdapter;
91import org.eclipse.swt.events.FocusEvent;
92import org.eclipse.swt.events.KeyAdapter;
93import org.eclipse.swt.events.KeyEvent;
94import org.eclipse.swt.events.MouseAdapter;
95import org.eclipse.swt.events.MouseEvent;
96import org.eclipse.swt.events.SelectionAdapter;
97import org.eclipse.swt.events.SelectionEvent;
98import org.eclipse.swt.graphics.Color;
99import org.eclipse.swt.graphics.Font;
100import org.eclipse.swt.graphics.Image;
101import org.eclipse.swt.graphics.Point;
102import org.eclipse.swt.graphics.Rectangle;
103import org.eclipse.swt.layout.FillLayout;
104import org.eclipse.swt.layout.GridData;
105import org.eclipse.swt.layout.GridLayout;
106import org.eclipse.swt.widgets.Composite;
107import org.eclipse.swt.widgets.Display;
108import org.eclipse.swt.widgets.Event;
109import org.eclipse.swt.widgets.Label;
110import org.eclipse.swt.widgets.Listener;
111import org.eclipse.swt.widgets.Menu;
112import org.eclipse.swt.widgets.MessageBox;
113import org.eclipse.swt.widgets.Shell;
114import org.eclipse.swt.widgets.TableColumn;
115import org.eclipse.swt.widgets.TableItem;
116import org.eclipse.swt.widgets.Text;
117import org.eclipse.ui.PlatformUI;
118import org.eclipse.ui.ide.IGotoMarker;
119import org.eclipse.ui.themes.ColorUtil;
120
121/**
122 * The generic TMF Events table
123 *
124 * This is a view that will list events that are read from a trace.
125 *
126 * @version 1.0
127 * @author Francois Chouinard
128 * @author Patrick Tasse
93bfd50a 129 * @since 2.0
db4721fb 130 */
faa38350 131public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorSettingsListener, ISelectionProvider {
db4721fb
PT
132
133 private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
134 "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
135 private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$
136 private static final Image SEARCH_MATCH_IMAGE = Activator.getDefault().getImageFromPath(
137 "icons/elcl16/search_match.gif"); //$NON-NLS-1$
138 private static final Image SEARCH_MATCH_BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
139 "icons/elcl16/search_match_bookmark.gif"); //$NON-NLS-1$
140 private static final Image FILTER_IMAGE = Activator.getDefault()
141 .getImageFromPath("icons/elcl16/filter_items.gif"); //$NON-NLS-1$
142 private static final Image STOP_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/stop.gif"); //$NON-NLS-1$
143 private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;
144 private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;
145 private static final int MAX_CACHE_SIZE = 1000;
146
147 /**
148 * The events table search/filter keys
149 *
150 * @version 1.0
151 * @author Patrick Tasse
152 */
153 public interface Key {
154 /** Search text */
155 String SEARCH_TXT = "$srch_txt"; //$NON-NLS-1$
156
157 /** Search object */
158 String SEARCH_OBJ = "$srch_obj"; //$NON-NLS-1$
159
160 /** Filter text */
161 String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$
162
163 /** Filter object */
164 String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$
165
166 /** Timestamp*/
167 String TIMESTAMP = "$time"; //$NON-NLS-1$
168
169 /** Rank */
170 String RANK = "$rank"; //$NON-NLS-1$
171
172 /** Field ID */
173 String FIELD_ID = "$field_id"; //$NON-NLS-1$
174
175 /** Bookmark indicator */
176 String BOOKMARK = "$bookmark"; //$NON-NLS-1$
177 }
178
179 /**
180 * The events table search/filter state
181 *
182 * @version 1.0
183 * @author Patrick Tasse
184 */
185 public static enum HeaderState {
186 /** A search is being run */
187 SEARCH,
188
189 /** A filter is applied */
190 FILTER
191 }
192
193 interface Direction {
194 int FORWARD = +1;
195 int BACKWARD = -1;
196 }
197
198 // ------------------------------------------------------------------------
199 // Table data
200 // ------------------------------------------------------------------------
201
202 protected Composite fComposite;
203 protected SashForm fSashForm;
204 protected TmfVirtualTable fTable;
205 protected TmfRawEventViewer fRawViewer;
206 protected ITmfTrace fTrace;
207 protected boolean fPackDone = false;
208 protected HeaderState fHeaderState = HeaderState.SEARCH;
209 protected long fSelectedRank = 0;
210
211 // Filter data
212 protected long fFilterMatchCount;
213 protected long fFilterCheckCount;
214 protected FilterThread fFilterThread;
215 protected boolean fFilterThreadResume = false;
216 protected final Object fFilterSyncObj = new Object();
217 protected SearchThread fSearchThread;
218 protected final Object fSearchSyncObj = new Object();
db4721fb 219
93bfd50a
PT
220 /**
221 * List of selection change listeners (element type: <code>ISelectionChangedListener</code>).
222 *
223 * @see #fireSelectionChanged
224 */
225 private ListenerList selectionChangedListeners = new ListenerList();
226
db4721fb
PT
227 // Bookmark map <Rank, MarkerId>
228 protected Map<Long, Long> fBookmarksMap = new HashMap<Long, Long>();
229 protected IFile fBookmarksFile;
230 protected long fPendingGotoRank = -1;
231
232 // SWT resources
233 protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
234 protected Color fGrayColor;
235 protected Color fGreenColor;
236 protected Font fBoldFont;
237
238 // Table column names
239 static private final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,
240 Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,
241 Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };
242
243 static private final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),
244 new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),
245 new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT) };
246
247 // Event cache
248 private final TmfEventsCache fCache;
249 private boolean fCacheUpdateBusy = false;
250 private boolean fCacheUpdatePending = false;
251 private boolean fCacheUpdateCompleted = false;
252 private final Object fCacheUpdateSyncObj = new Object();
253
254 private boolean fDisposeOnClose;
255
256 // ------------------------------------------------------------------------
257 // Constructor
258 // ------------------------------------------------------------------------
259
260 /**
261 * Basic constructor, will use default column data.
262 *
263 * @param parent
264 * The parent composite UI object
265 * @param cacheSize
266 * The size of the event table cache
267 */
268 public TmfEventsTable(final Composite parent, final int cacheSize) {
269 this(parent, cacheSize, COLUMN_DATA);
270 }
271
272 /**
273 * Advanced constructor, where we also define which column data to use.
274 *
275 * @param parent
276 * The parent composite UI object
277 * @param cacheSize
278 * The size of the event table cache
279 * @param columnData
280 * The column data to use for this table
281 */
282 public TmfEventsTable(final Composite parent, int cacheSize, final ColumnData[] columnData) {
283 super("TmfEventsTable"); //$NON-NLS-1$
284
285 fComposite = new Composite(parent, SWT.NONE);
286 final GridLayout gl = new GridLayout(1, false);
287 gl.marginHeight = 0;
288 gl.marginWidth = 0;
289 gl.verticalSpacing = 0;
290 fComposite.setLayout(gl);
291
292 fSashForm = new SashForm(fComposite, SWT.HORIZONTAL);
293 fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
294
295 // Create a virtual table
296 final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
297 fTable = new TmfVirtualTable(fSashForm, style);
298
299 // Set the table layout
300 final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
301 fTable.setLayoutData(layoutData);
302
303 // Some cosmetic enhancements
304 fTable.setHeaderVisible(true);
305 fTable.setLinesVisible(true);
306
307 // Set the columns
308 setColumnHeaders(columnData);
309
310 // Set the default column field ids if this is not a subclass
311 if (Arrays.equals(columnData, COLUMN_DATA)) {
312 fTable.getColumns()[0].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TIMESTAMP);
313 fTable.getColumns()[1].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_SOURCE);
314 fTable.getColumns()[2].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TYPE);
315 fTable.getColumns()[3].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_REFERENCE);
316 fTable.getColumns()[4].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_CONTENT);
317 }
318
319 // Set the frozen row for header row
320 fTable.setFrozenRowCount(1);
321
322 // Create the header row cell editor
323 createHeaderEditor();
324
325 // Handle the table item selection
326 fTable.addSelectionListener(new SelectionAdapter() {
327 @Override
328 public void widgetSelected(final SelectionEvent e) {
329 final TableItem[] selection = fTable.getSelection();
330 if (selection.length > 0) {
331 final TableItem selectedTableItem = selection[0];
332 if (selectedTableItem != null) {
333 if (selectedTableItem.getData(Key.RANK) instanceof Long) {
334 fSelectedRank = (Long) selectedTableItem.getData(Key.RANK);
335 fRawViewer.selectAndReveal((Long) selectedTableItem.getData(Key.RANK));
336 }
337 if (selectedTableItem.getData(Key.TIMESTAMP) instanceof TmfTimestamp) {
338 final TmfTimestamp ts = (TmfTimestamp) selectedTableItem.getData(Key.TIMESTAMP);
339 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));
340 }
341 }
342 }
93bfd50a 343 fireSelectionChanged(new SelectionChangedEvent(TmfEventsTable.this, getSelection()));
db4721fb
PT
344 }
345 });
346
347 cacheSize = Math.max(cacheSize, Display.getDefault().getBounds().height / fTable.getItemHeight());
348 cacheSize = Math.min(cacheSize, MAX_CACHE_SIZE);
349 fCache = new TmfEventsCache(cacheSize, this);
350
351 // Handle the table item requests
352 fTable.addListener(SWT.SetData, new Listener() {
353
354 @Override
355 public void handleEvent(final Event event) {
356
357 final TableItem item = (TableItem) event.item;
358 int index = event.index - 1; // -1 for the header row
359
360 if (event.index == 0) {
361 setHeaderRowItemData(item);
362 return;
363 }
364
365 if (fTable.getData(Key.FILTER_OBJ) != null) {
366 if ((event.index == 1) || (event.index == (fTable.getItemCount() - 1))) {
367 setFilterStatusRowItemData(item);
368 return;
369 }
370 index = index - 1; // -1 for top filter status row
371 }
372
373 final CachedEvent cachedEvent = fCache.getEvent(index);
374 if (cachedEvent != null) {
375 setItemData(item, cachedEvent.event, cachedEvent.rank);
376 return;
377 }
378
379 // Else, fill the cache asynchronously (and off the UI thread)
380 event.doit = false;
381 }
382 });
383
384 fTable.addMouseListener(new MouseAdapter() {
385 @Override
386 public void mouseDoubleClick(final MouseEvent event) {
387 if (event.button != 1) {
388 return;
389 }
390 // Identify the selected row
391 final Point point = new Point(event.x, event.y);
392 final TableItem item = fTable.getItem(point);
393 if (item != null) {
394 final Rectangle imageBounds = item.getImageBounds(0);
395 imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
396 if (imageBounds.contains(point)) {
397 final Long rank = (Long) item.getData(Key.RANK);
398 if (rank != null) {
399 toggleBookmark(rank);
400 }
401 }
402 }
403 }
404 });
405
406 final Listener tooltipListener = new Listener () {
407 Shell tooltipShell = null;
408 @Override
409 public void handleEvent(final Event event) {
410 switch (event.type) {
411 case SWT.MouseHover:
412 final TableItem item = fTable.getItem(new Point(event.x, event.y));
413 if (item == null) {
414 return;
415 }
416 final Long rank = (Long) item.getData(Key.RANK);
417 if (rank == null) {
418 return;
419 }
420 final String tooltipText = (String) item.getData(Key.BOOKMARK);
421 final Rectangle bounds = item.getImageBounds(0);
422 bounds.width = BOOKMARK_IMAGE.getBounds().width;
423 if (!bounds.contains(event.x,event.y)) {
424 return;
425 }
426 if ((tooltipShell != null) && !tooltipShell.isDisposed()) {
427 tooltipShell.dispose();
428 }
429 tooltipShell = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
430 tooltipShell.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
431 final FillLayout layout = new FillLayout();
432 layout.marginWidth = 2;
433 tooltipShell.setLayout(layout);
434 final Label label = new Label(tooltipShell, SWT.WRAP);
435 String text = rank.toString() + (tooltipText != null ? ": " + tooltipText : ""); //$NON-NLS-1$ //$NON-NLS-2$
436 label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
437 label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
438 label.setText(text);
439 label.addListener(SWT.MouseExit, this);
440 label.addListener(SWT.MouseDown, this);
441 label.addListener(SWT.MouseWheel, this);
442 final Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
443 /*
444 * Bug in Linux. The coordinates of the event have an origin that excludes the table header but
445 * the method toDisplay() expects coordinates relative to an origin that includes the table header.
446 */
447 int y = event.y;
448 if (System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
449 y += fTable.getHeaderHeight();
450 }
451 Point pt = fTable.toDisplay(event.x, y);
452 pt.x += BOOKMARK_IMAGE.getBounds().width;
3be2946f 453 pt.y += item.getBounds().height;
db4721fb
PT
454 tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
455 tooltipShell.setVisible(true);
456 break;
457 case SWT.Dispose:
458 case SWT.KeyDown:
459 case SWT.MouseMove:
460 case SWT.MouseExit:
461 case SWT.MouseDown:
462 case SWT.MouseWheel:
463 if (tooltipShell != null) {
464 tooltipShell.dispose();
465 tooltipShell = null;
466 }
467 break;
468 default:
469 break;
470 }
471 }
472 };
473
474 fTable.addListener(SWT.MouseHover, tooltipListener);
475 fTable.addListener(SWT.Dispose, tooltipListener);
476 fTable.addListener(SWT.KeyDown, tooltipListener);
477 fTable.addListener(SWT.MouseMove, tooltipListener);
478 fTable.addListener(SWT.MouseExit, tooltipListener);
479 fTable.addListener(SWT.MouseDown, tooltipListener);
480 fTable.addListener(SWT.MouseWheel, tooltipListener);
481
482 // Create resources
483 createResources();
484
485 ColorSettingsManager.addColorSettingsListener(this);
486
487 fTable.setItemCount(1); // +1 for header row
488
489 fRawViewer = new TmfRawEventViewer(fSashForm, SWT.H_SCROLL | SWT.V_SCROLL);
490
491 fRawViewer.addSelectionListener(new Listener() {
492 @Override
493 public void handleEvent(final Event e) {
494 if (e.data instanceof Long) {
495 final long rank = (Long) e.data;
496 int index = (int) rank;
497 if (fTable.getData(Key.FILTER_OBJ) != null) {
498 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
499 }
500 fTable.setSelection(index + 1); // +1 for header row
501 fSelectedRank = rank;
1e1bef82 502 } else if (e.data instanceof ITmfLocation) {
db4721fb
PT
503 // DOES NOT WORK: rank undefined in context from seekLocation()
504 // ITmfLocation<?> location = (ITmfLocation<?>) e.data;
505 // TmfContext context = fTrace.seekLocation(location);
506 // fTable.setSelection((int) context.getRank());
507 return;
508 } else {
509 return;
510 }
511 final TableItem[] selection = fTable.getSelection();
512 if ((selection != null) && (selection.length > 0)) {
513 final TmfTimestamp ts = (TmfTimestamp) fTable.getSelection()[0].getData(Key.TIMESTAMP);
514 if (ts != null) {
515 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, ts));
516 }
517 }
518 }
519 });
520
521 fSashForm.setWeights(new int[] { 1, 1 });
522 fRawViewer.setVisible(false);
523
524 createPopupMenu();
525 }
526
527 protected void createPopupMenu() {
528 final IAction showTableAction = new Action(Messages.TmfEventsTable_ShowTableActionText) {
529 @Override
530 public void run() {
531 fTable.setVisible(true);
532 fSashForm.layout();
533 }
534 };
535
536 final IAction hideTableAction = new Action(Messages.TmfEventsTable_HideTableActionText) {
537 @Override
538 public void run() {
539 fTable.setVisible(false);
540 fSashForm.layout();
541 }
542 };
543
544 final IAction showRawAction = new Action(Messages.TmfEventsTable_ShowRawActionText) {
545 @Override
546 public void run() {
547 fRawViewer.setVisible(true);
548 fSashForm.layout();
549 final int index = fTable.getSelectionIndex();
550 if (index >= +1) {
551 fRawViewer.selectAndReveal(index - 1);
552 }
553 }
554 };
555
556 final IAction hideRawAction = new Action(Messages.TmfEventsTable_HideRawActionText) {
557 @Override
558 public void run() {
559 fRawViewer.setVisible(false);
560 fSashForm.layout();
561 }
562 };
563
564 final IAction showSearchBarAction = new Action(Messages.TmfEventsTable_ShowSearchBarActionText) {
565 @Override
566 public void run() {
567 fHeaderState = HeaderState.SEARCH;
568 fTable.refresh();
569 }
570 };
571
572 final IAction showFilterBarAction = new Action(Messages.TmfEventsTable_ShowFilterBarActionText) {
573 @Override
574 public void run() {
575 fHeaderState = HeaderState.FILTER;
576 fTable.refresh();
577 }
578 };
579
580 final IAction clearFiltersAction = new Action(Messages.TmfEventsTable_ClearFiltersActionText) {
581 @Override
582 public void run() {
583 clearFilters();
584 }
585 };
586
587 class ToggleBookmarkAction extends Action {
588 long fRank;
589
590 public ToggleBookmarkAction(final String text, final long rank) {
591 super(text);
592 fRank = rank;
593 }
594
595 @Override
596 public void run() {
597 toggleBookmark(fRank);
598 }
599 }
600
601 final MenuManager tablePopupMenu = new MenuManager();
602 tablePopupMenu.setRemoveAllWhenShown(true);
603 tablePopupMenu.addMenuListener(new IMenuListener() {
604 @Override
605 public void menuAboutToShow(final IMenuManager manager) {
606 if (fTable.getSelectionIndex() == 0) {
607 // Right-click on header row
608 if (fHeaderState == HeaderState.FILTER) {
609 tablePopupMenu.add(showSearchBarAction);
610 } else {
611 tablePopupMenu.add(showFilterBarAction);
612 }
613 return;
614 }
615 final Point point = fTable.toControl(Display.getDefault().getCursorLocation());
616 final TableItem item = fTable.getSelection().length > 0 ? fTable.getSelection()[0] : null;
617 if (item != null) {
618 final Rectangle imageBounds = item.getImageBounds(0);
619 imageBounds.width = BOOKMARK_IMAGE.getBounds().width;
620 if (point.x <= (imageBounds.x + imageBounds.width)) {
621 // Right-click on left margin
622 final Long rank = (Long) item.getData(Key.RANK);
623 if ((rank != null) && (fBookmarksFile != null)) {
624 if (fBookmarksMap.containsKey(rank)) {
625 tablePopupMenu.add(new ToggleBookmarkAction(
626 Messages.TmfEventsTable_RemoveBookmarkActionText, rank));
627 } else {
628 tablePopupMenu.add(new ToggleBookmarkAction(
629 Messages.TmfEventsTable_AddBookmarkActionText, rank));
630 }
631 }
632 return;
633 }
634 }
635 // Right-click on table
636 if (fTable.isVisible() && fRawViewer.isVisible()) {
637 tablePopupMenu.add(hideTableAction);
638 tablePopupMenu.add(hideRawAction);
639 } else if (!fTable.isVisible()) {
640 tablePopupMenu.add(showTableAction);
641 } else if (!fRawViewer.isVisible()) {
642 tablePopupMenu.add(showRawAction);
643 }
644 tablePopupMenu.add(new Separator());
645 tablePopupMenu.add(clearFiltersAction);
646 final ITmfFilterTreeNode[] savedFilters = FilterManager.getSavedFilters();
647 if (savedFilters.length > 0) {
648 final MenuManager subMenu = new MenuManager(Messages.TmfEventsTable_ApplyPresetFilterMenuName);
649 for (final ITmfFilterTreeNode node : savedFilters) {
650 if (node instanceof TmfFilterNode) {
651 final TmfFilterNode filter = (TmfFilterNode) node;
652 subMenu.add(new Action(filter.getFilterName()) {
653 @Override
654 public void run() {
655 applyFilter(filter);
656 }
657 });
658 }
659 }
660 tablePopupMenu.add(subMenu);
661 }
662 appendToTablePopupMenu(tablePopupMenu, item);
663 }
664 });
665
666 final MenuManager rawViewerPopupMenu = new MenuManager();
667 rawViewerPopupMenu.setRemoveAllWhenShown(true);
668 rawViewerPopupMenu.addMenuListener(new IMenuListener() {
669 @Override
670 public void menuAboutToShow(final IMenuManager manager) {
671 if (fTable.isVisible() && fRawViewer.isVisible()) {
672 rawViewerPopupMenu.add(hideTableAction);
673 rawViewerPopupMenu.add(hideRawAction);
674 } else if (!fTable.isVisible()) {
675 rawViewerPopupMenu.add(showTableAction);
676 } else if (!fRawViewer.isVisible()) {
677 rawViewerPopupMenu.add(showRawAction);
678 }
679 appendToRawPopupMenu(tablePopupMenu);
680 }
681 });
682
683 Menu menu = tablePopupMenu.createContextMenu(fTable);
684 fTable.setMenu(menu);
685
686 menu = rawViewerPopupMenu.createContextMenu(fRawViewer);
687 fRawViewer.setMenu(menu);
688 }
689
f8177ba2
FC
690
691 @SuppressWarnings("unused")
db4721fb
PT
692 protected void appendToTablePopupMenu(final MenuManager tablePopupMenu, final TableItem selectedItem) {
693 // override to append more actions
694 }
695
f8177ba2 696 @SuppressWarnings("unused")
db4721fb
PT
697 protected void appendToRawPopupMenu(final MenuManager rawViewerPopupMenu) {
698 // override to append more actions
699 }
700
701 @Override
702 public void dispose() {
703 stopSearchThread();
704 stopFilterThread();
705 ColorSettingsManager.removeColorSettingsListener(this);
706 fComposite.dispose();
707 if ((fTrace != null) && fDisposeOnClose) {
708 fTrace.dispose();
709 }
710 fResourceManager.dispose();
711 super.dispose();
712 }
713
714 /**
715 * Assign a layout data object to this view.
716 *
717 * @param layoutData
718 * The layout data to assign
719 */
720 public void setLayoutData(final Object layoutData) {
721 fComposite.setLayoutData(layoutData);
722 }
723
724 /**
725 * Get the virtual table contained in this event table.
726 *
727 * @return The TMF virtual table
728 */
729 public TmfVirtualTable getTable() {
730 return fTable;
731 }
732
733 /**
734 * @param columnData
735 *
736 * FIXME: Add support for column selection
737 */
738 protected void setColumnHeaders(final ColumnData[] columnData) {
739 fTable.setColumnHeaders(columnData);
740 }
741
742 protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
743 final ITmfEventField[] fields = extractItemFields(event);
744 final String[] content = new String[fields.length];
745 for (int i = 0; i < fields.length; i++) {
746 content[i] = fields[i].getValue() != null ? fields[i].getValue().toString() : ""; //$NON-NLS-1$
747 }
748 item.setText(content);
93bfd50a 749 item.setData(event);
db4721fb
PT
750 item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
751 item.setData(Key.RANK, rank);
752
753 boolean bookmark = false;
754 final Long markerId = fBookmarksMap.get(rank);
755 if (markerId != null) {
756 bookmark = true;
757 try {
758 final IMarker marker = fBookmarksFile.findMarker(markerId);
759 item.setData(Key.BOOKMARK, marker.getAttribute(IMarker.MESSAGE));
760 } catch (final CoreException e) {
761 displayException(e);
762 }
763 } else {
764 item.setData(Key.BOOKMARK, null);
765 }
766
767 boolean searchMatch = false;
768 boolean searchNoMatch = false;
769 final ITmfFilter searchFilter = (ITmfFilter) fTable.getData(Key.SEARCH_OBJ);
770 if (searchFilter != null) {
771 if (searchFilter.matches(event)) {
772 searchMatch = true;
773 } else {
774 searchNoMatch = true;
775 }
776 }
777
778 final ColorSetting colorSetting = ColorSettingsManager.getColorSetting(event);
779 if (searchNoMatch) {
780 item.setForeground(colorSetting.getDimmedForegroundColor());
781 item.setBackground(colorSetting.getDimmedBackgroundColor());
782 } else {
783 item.setForeground(colorSetting.getForegroundColor());
784 item.setBackground(colorSetting.getBackgroundColor());
785 }
786
787 if (searchMatch) {
788 if (bookmark) {
789 item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
790 } else {
791 item.setImage(SEARCH_MATCH_IMAGE);
792 }
793 } else if (bookmark) {
794 item.setImage(BOOKMARK_IMAGE);
795 } else {
796 item.setImage((Image) null);
797 }
798 }
799
800 protected void setHeaderRowItemData(final TableItem item) {
801 String txtKey = null;
802 if (fHeaderState == HeaderState.SEARCH) {
803 item.setImage(SEARCH_IMAGE);
804 txtKey = Key.SEARCH_TXT;
805 } else if (fHeaderState == HeaderState.FILTER) {
806 item.setImage(FILTER_IMAGE);
807 txtKey = Key.FILTER_TXT;
808 }
809 item.setForeground(fGrayColor);
810 for (int i = 0; i < fTable.getColumns().length; i++) {
811 final TableColumn column = fTable.getColumns()[i];
812 final String filter = (String) column.getData(txtKey);
813 if (filter == null) {
814 if (fHeaderState == HeaderState.SEARCH) {
815 item.setText(i, SEARCH_HINT);
816 } else if (fHeaderState == HeaderState.FILTER) {
817 item.setText(i, FILTER_HINT);
818 }
819 item.setForeground(i, fGrayColor);
820 item.setFont(i, fTable.getFont());
821 } else {
822 item.setText(i, filter);
823 item.setForeground(i, fGreenColor);
824 item.setFont(i, fBoldFont);
825 }
826 }
827 }
828
829 protected void setFilterStatusRowItemData(final TableItem item) {
830 for (int i = 0; i < fTable.getColumns().length; i++) {
831 if (i == 0) {
832 if ((fTrace == null) || (fFilterCheckCount == fTrace.getNbEvents())) {
833 item.setImage(FILTER_IMAGE);
834 } else {
835 item.setImage(STOP_IMAGE);
836 }
837 item.setText(0, fFilterMatchCount + "/" + fFilterCheckCount); //$NON-NLS-1$
838 } else {
839 item.setText(i, ""); //$NON-NLS-1$
840 }
841 }
93bfd50a 842 item.setData(null);
db4721fb
PT
843 item.setData(Key.TIMESTAMP, null);
844 item.setData(Key.RANK, null);
845 item.setForeground(null);
846 item.setBackground(null);
847 }
848
849 protected void createHeaderEditor() {
850 final TableEditor tableEditor = fTable.createTableEditor();
851 tableEditor.horizontalAlignment = SWT.LEFT;
852 tableEditor.verticalAlignment = SWT.CENTER;
853 tableEditor.grabHorizontal = true;
854 tableEditor.minimumWidth = 50;
855
856 // Handle the header row selection
857 fTable.addMouseListener(new MouseAdapter() {
858 int columnIndex;
859 TableColumn column;
860 TableItem item;
861
862 @Override
863 public void mouseDown(final MouseEvent event) {
864 if (event.button != 1) {
865 return;
866 }
867 // Identify the selected row
868 final Point point = new Point(event.x, event.y);
869 item = fTable.getItem(point);
870
871 // Header row selected
872 if ((item != null) && (fTable.indexOf(item) == 0)) {
873
874 // Icon selected
875 if (item.getImageBounds(0).contains(point)) {
876 if (fHeaderState == HeaderState.SEARCH) {
877 fHeaderState = HeaderState.FILTER;
878 } else if (fHeaderState == HeaderState.FILTER) {
879 fHeaderState = HeaderState.SEARCH;
880 }
881 fTable.refresh();
882 return;
883 }
884
885 // Identify the selected column
886 columnIndex = -1;
887 for (int i = 0; i < fTable.getColumns().length; i++) {
888 final Rectangle rect = item.getBounds(i);
889 if (rect.contains(point)) {
890 columnIndex = i;
891 break;
892 }
893 }
894
895 if (columnIndex == -1) {
896 return;
897 }
898
899 column = fTable.getColumns()[columnIndex];
900
901 String txtKey = null;
902 if (fHeaderState == HeaderState.SEARCH) {
903 txtKey = Key.SEARCH_TXT;
904 } else if (fHeaderState == HeaderState.FILTER) {
905 txtKey = Key.FILTER_TXT;
906 }
907
908 // The control that will be the editor must be a child of the Table
909 final Text newEditor = (Text) fTable.createTableEditorControl(Text.class);
910 final String headerString = (String) column.getData(txtKey);
911 if (headerString != null) {
912 newEditor.setText(headerString);
913 }
914 newEditor.addFocusListener(new FocusAdapter() {
915 @Override
916 public void focusLost(final FocusEvent e) {
917 final boolean changed = updateHeader(newEditor.getText());
918 if (changed) {
919 applyHeader();
920 }
921 }
922 });
923 newEditor.addKeyListener(new KeyAdapter() {
924 @Override
925 public void keyPressed(final KeyEvent e) {
926 if (e.character == SWT.CR) {
927 updateHeader(newEditor.getText());
928 applyHeader();
929 } else if (e.character == SWT.ESC) {
930 tableEditor.getEditor().dispose();
931 }
932 }
933 });
934 newEditor.selectAll();
935 newEditor.setFocus();
936 tableEditor.setEditor(newEditor, item, columnIndex);
937 }
938 }
939
940 /*
941 * returns true is value was changed
942 */
943 private boolean updateHeader(final String text) {
944 String objKey = null;
945 String txtKey = null;
946 if (fHeaderState == HeaderState.SEARCH) {
947 objKey = Key.SEARCH_OBJ;
948 txtKey = Key.SEARCH_TXT;
949 } else if (fHeaderState == HeaderState.FILTER) {
950 objKey = Key.FILTER_OBJ;
951 txtKey = Key.FILTER_TXT;
952 }
953 if (text.trim().length() > 0) {
954 try {
955 final String regex = TmfFilterMatchesNode.regexFix(text);
956 Pattern.compile(regex);
957 if (regex.equals(column.getData(txtKey))) {
958 tableEditor.getEditor().dispose();
959 return false;
960 }
961 final TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
962 String fieldId = (String) column.getData(Key.FIELD_ID);
963 if (fieldId == null) {
964 fieldId = column.getText();
965 }
966 filter.setField(fieldId);
967 filter.setRegex(regex);
968 column.setData(objKey, filter);
969 column.setData(txtKey, regex);
970 } catch (final PatternSyntaxException ex) {
971 tableEditor.getEditor().dispose();
972 MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
973 ex.getDescription(), ex.getMessage());
974 return false;
975 }
976 } else {
977 if (column.getData(txtKey) == null) {
978 tableEditor.getEditor().dispose();
979 return false;
980 }
981 column.setData(objKey, null);
982 column.setData(txtKey, null);
983 }
984 return true;
985 }
986
987 private void applyHeader() {
988 if (fHeaderState == HeaderState.SEARCH) {
989 stopSearchThread();
990 final TmfFilterAndNode filter = new TmfFilterAndNode(null);
991 for (final TableColumn column : fTable.getColumns()) {
992 final Object filterObj = column.getData(Key.SEARCH_OBJ);
993 if (filterObj instanceof ITmfFilterTreeNode) {
994 filter.addChild((ITmfFilterTreeNode) filterObj);
995 }
996 }
997 if (filter.getChildrenCount() > 0) {
998 fTable.setData(Key.SEARCH_OBJ, filter);
999 fTable.refresh();
1000 searchNext();
1001 fireSearchApplied(filter);
1002 } else {
1003 fTable.setData(Key.SEARCH_OBJ, null);
1004 fTable.refresh();
1005 fireSearchApplied(null);
1006 }
1007 } else if (fHeaderState == HeaderState.FILTER) {
1008 final TmfFilterAndNode filter = new TmfFilterAndNode(null);
1009 for (final TableColumn column : fTable.getColumns()) {
1010 final Object filterObj = column.getData(Key.FILTER_OBJ);
1011 if (filterObj instanceof ITmfFilterTreeNode) {
1012 filter.addChild((ITmfFilterTreeNode) filterObj);
1013 }
1014 }
1015 if (filter.getChildrenCount() > 0) {
1016 applyFilter(filter);
1017 } else {
1018 clearFilters();
1019 }
1020 }
1021
1022 tableEditor.getEditor().dispose();
1023 }
1024 });
1025
1026 fTable.addKeyListener(new KeyAdapter() {
1027 @Override
1028 public void keyPressed(final KeyEvent e) {
1029 e.doit = false;
1030 if (e.character == SWT.ESC) {
1031 stopFilterThread();
1032 stopSearchThread();
1033 fTable.refresh();
1034 } else if (e.character == SWT.DEL) {
1035 if (fHeaderState == HeaderState.SEARCH) {
1036 stopSearchThread();
1037 for (final TableColumn column : fTable.getColumns()) {
1038 column.setData(Key.SEARCH_OBJ, null);
1039 column.setData(Key.SEARCH_TXT, null);
1040 }
1041 fTable.setData(Key.SEARCH_OBJ, null);
1042 fTable.refresh();
1043 fireSearchApplied(null);
1044 } else if (fHeaderState == HeaderState.FILTER) {
1045 clearFilters();
1046 }
1047 } else if (e.character == SWT.CR) {
1048 if ((e.stateMask & SWT.SHIFT) == 0) {
1049 searchNext();
1050 } else {
1051 searchPrevious();
1052 }
1053 }
1054 }
1055 });
1056 }
1057
1058 protected void fireFilterApplied(final ITmfFilter filter) {
faa38350 1059 broadcast(new TmfEventFilterAppliedSignal(this, fTrace, filter));
db4721fb
PT
1060 }
1061
1062 protected void fireSearchApplied(final ITmfFilter filter) {
faa38350 1063 broadcast(new TmfEventSearchAppliedSignal(this, fTrace, filter));
db4721fb
PT
1064 }
1065
1066 protected void startFilterThread() {
1067 synchronized (fFilterSyncObj) {
1068 final ITmfFilterTreeNode filter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1069 if (fFilterThread == null || fFilterThread.filter != filter) {
1070 if (fFilterThread != null) {
1071 fFilterThread.cancel();
1072 fFilterThreadResume = false;
1073 }
1074 fFilterThread = new FilterThread(filter);
1075 fFilterThread.start();
1076 } else {
1077 fFilterThreadResume = true;
1078 }
1079 }
1080 }
1081
1082 protected void stopFilterThread() {
1083 synchronized (fFilterSyncObj) {
1084 if (fFilterThread != null) {
1085 fFilterThread.cancel();
1086 fFilterThread = null;
1087 fFilterThreadResume = false;
1088 }
1089 }
1090 }
1091
1092 /**
1093 * @since 1.1
1094 */
1095 protected void applyFilter(ITmfFilter filter) {
1096 stopFilterThread();
1097 stopSearchThread();
1098 fFilterMatchCount = 0;
1099 fFilterCheckCount = 0;
1100 fCache.applyFilter(filter);
1101 fTable.clearAll();
1102 fTable.setData(Key.FILTER_OBJ, filter);
1103 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1104 startFilterThread();
1105 fireFilterApplied(filter);
1106 }
1107
1108 protected void clearFilters() {
1109 if (fTable.getData(Key.FILTER_OBJ) == null) {
1110 return;
1111 }
1112 stopFilterThread();
1113 stopSearchThread();
1114 fCache.clearFilter();
1115 fTable.clearAll();
1116 for (final TableColumn column : fTable.getColumns()) {
1117 column.setData(Key.FILTER_OBJ, null);
1118 column.setData(Key.FILTER_TXT, null);
1119 }
1120 fTable.setData(Key.FILTER_OBJ, null);
1121 if (fTrace != null) {
1122 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1123 } else {
1124 fTable.setItemCount(1); // +1 for header row
1125 }
1126 fFilterMatchCount = 0;
1127 fFilterCheckCount = 0;
1128 if (fSelectedRank >= 0) {
1129 fTable.setSelection((int) fSelectedRank + 1); // +1 for header row
1130 } else {
1131 fTable.setSelection(0);
1132 }
1133 fireFilterApplied(null);
1134 }
1135
1136 protected class FilterThread extends Thread {
1137 private final ITmfFilterTreeNode filter;
1138 private TmfDataRequest request;
1139 private boolean refreshBusy = false;
1140 private boolean refreshPending = false;
1141 private final Object syncObj = new Object();
1142
1143 public FilterThread(final ITmfFilterTreeNode filter) {
1144 super("Filter Thread"); //$NON-NLS-1$
1145 this.filter = filter;
1146 }
1147
1148 @Override
1149 public void run() {
1150 if (fTrace == null) {
1151 return;
1152 }
1153 final int nbRequested = (int) (fTrace.getNbEvents() - fFilterCheckCount);
1154 if (nbRequested <= 0) {
1155 return;
1156 }
1157 request = new TmfDataRequest(ITmfEvent.class, (int) fFilterCheckCount,
1158 nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1159 @Override
1160 public void handleData(final ITmfEvent event) {
1161 super.handleData(event);
1162 if (request.isCancelled()) {
1163 return;
1164 }
1165 if (filter.matches(event)) {
1166 final long rank = fFilterCheckCount;
1167 final int index = (int) fFilterMatchCount;
1168 fFilterMatchCount++;
1169 fCache.storeEvent(event.clone(), rank, index);
1170 refreshTable();
1171 } else if ((fFilterCheckCount % 100) == 0) {
1172 refreshTable();
1173 }
1174 fFilterCheckCount++;
1175 }
1176 };
1177 ((ITmfDataProvider) fTrace).sendRequest(request);
1178 try {
1179 request.waitForCompletion();
1180 } catch (final InterruptedException e) {
1181 }
1182 refreshTable();
1183 synchronized (fFilterSyncObj) {
1184 fFilterThread = null;
1185 if (fFilterThreadResume) {
1186 fFilterThreadResume = false;
1187 fFilterThread = new FilterThread(filter);
1188 fFilterThread.start();
1189 }
1190 }
1191 }
1192
1193 public void refreshTable() {
1194 synchronized (syncObj) {
1195 if (refreshBusy) {
1196 refreshPending = true;
1197 return;
1198 }
1199 refreshBusy = true;
1200 }
1201 Display.getDefault().asyncExec(new Runnable() {
1202 @Override
1203 public void run() {
1204 if (request.isCancelled()) {
1205 return;
1206 }
1207 if (fTable.isDisposed()) {
1208 return;
1209 }
1210 fTable.setItemCount((int) fFilterMatchCount + 3); // +1 for header row, +2 for top and bottom filter status rows
1211 fTable.refresh();
1212 synchronized (syncObj) {
1213 refreshBusy = false;
1214 if (refreshPending) {
1215 refreshPending = false;
1216 refreshTable();
1217 }
1218 }
1219 }
1220 });
1221 }
1222
1223 public void cancel() {
1224 if (request != null) {
1225 request.cancel();
1226 }
1227 }
1228 }
1229
1230 protected void searchNext() {
1231 synchronized (fSearchSyncObj) {
1232 if (fSearchThread != null) {
1233 return;
1234 }
1235 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1236 if (searchFilter == null) {
1237 return;
1238 }
1239 final int selectionIndex = fTable.getSelectionIndex();
1240 int startIndex;
1241 if (selectionIndex > 0) {
1242 startIndex = selectionIndex; // -1 for header row, +1 for next event
1243 } else {
1244 // header row is selected, start at top event
1245 startIndex = Math.max(0, fTable.getTopIndex() - 1); // -1 for header row
1246 }
1247 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1248 if (eventFilter != null)
1249 {
1250 startIndex = Math.max(0, startIndex - 1); // -1 for top filter status row
1251 }
1252 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.FORWARD);
1253 fSearchThread.schedule();
1254 }
1255 }
1256
1257 protected void searchPrevious() {
1258 synchronized (fSearchSyncObj) {
1259 if (fSearchThread != null) {
1260 return;
1261 }
1262 final ITmfFilterTreeNode searchFilter = (ITmfFilterTreeNode) fTable.getData(Key.SEARCH_OBJ);
1263 if (searchFilter == null) {
1264 return;
1265 }
1266 final int selectionIndex = fTable.getSelectionIndex();
1267 int startIndex;
1268 if (selectionIndex > 0) {
1269 startIndex = selectionIndex - 2; // -1 for header row, -1 for previous event
1270 } else {
1271 // header row is selected, start at precedent of top event
1272 startIndex = fTable.getTopIndex() - 2; // -1 for header row, -1 for previous event
1273 }
1274 final ITmfFilterTreeNode eventFilter = (ITmfFilterTreeNode) fTable.getData(Key.FILTER_OBJ);
1275 if (eventFilter != null)
1276 {
1277 startIndex = startIndex - 1; // -1 for top filter status row
1278 }
1279 fSearchThread = new SearchThread(searchFilter, eventFilter, startIndex, fSelectedRank, Direction.BACKWARD);
1280 fSearchThread.schedule();
1281 }
1282 }
1283
1284 protected void stopSearchThread() {
1285 fPendingGotoRank = -1;
1286 synchronized (fSearchSyncObj) {
1287 if (fSearchThread != null) {
1288 fSearchThread.cancel();
1289 fSearchThread = null;
1290 }
1291 }
1292 }
1293
1294 protected class SearchThread extends Job {
1295 protected ITmfFilterTreeNode searchFilter;
1296 protected ITmfFilterTreeNode eventFilter;
1297 protected int startIndex;
1298 protected int direction;
1299 protected long rank;
1300 protected long foundRank = -1;
1301 protected TmfDataRequest request;
ae3ffd37 1302 private ITmfTimestamp foundTimestamp = null;
db4721fb
PT
1303
1304 public SearchThread(final ITmfFilterTreeNode searchFilter, final ITmfFilterTreeNode eventFilter, final int startIndex,
1305 final long currentRank, final int direction) {
1306 super(Messages.TmfEventsTable_SearchingJobName);
1307 this.searchFilter = searchFilter;
1308 this.eventFilter = eventFilter;
1309 this.startIndex = startIndex;
1310 this.rank = currentRank;
1311 this.direction = direction;
1312 }
1313
1314 @Override
1315 protected IStatus run(final IProgressMonitor monitor) {
1316 if (fTrace == null) {
1317 return Status.OK_STATUS;
1318 }
1319 final Display display = Display.getDefault();
1320 if (startIndex < 0) {
1321 rank = (int) fTrace.getNbEvents() - 1;
1322 } else if (startIndex >= (fTable.getItemCount() - (eventFilter == null ? 1 : 3))) { // -1 for header row, -2 for top and bottom filter status rows
1323 rank = 0;
1324 } else {
1325 int idx = startIndex;
1326 while (foundRank == -1) {
1327 final CachedEvent event = fCache.peekEvent(idx);
1328 if (event == null) {
1329 break;
1330 }
1331 rank = event.rank;
1332 if (searchFilter.matches(event.event) && ((eventFilter == null) || eventFilter.matches(event.event))) {
1333 foundRank = event.rank;
ae3ffd37 1334 foundTimestamp = event.event.getTimestamp();
db4721fb
PT
1335 break;
1336 }
1337 if (direction == Direction.FORWARD) {
1338 idx++;
1339 } else {
1340 idx--;
1341 }
1342 }
1343 if (foundRank == -1) {
1344 if (direction == Direction.FORWARD) {
1345 rank++;
1346 if (rank > (fTrace.getNbEvents() - 1)) {
1347 rank = 0;
1348 }
1349 } else {
1350 rank--;
1351 if (rank < 0) {
1352 rank = (int) fTrace.getNbEvents() - 1;
1353 }
1354 }
1355 }
1356 }
1357 final int startRank = (int) rank;
1358 boolean wrapped = false;
1359 while (!monitor.isCanceled() && (foundRank == -1) && (fTrace != null)) {
1360 int nbRequested = (direction == Direction.FORWARD ? Integer.MAX_VALUE : Math.min((int) rank + 1, fTrace.getCacheSize()));
1361 if (direction == Direction.BACKWARD) {
1362 rank = Math.max(0, rank - fTrace.getCacheSize() + 1);
1363 }
1364 request = new TmfDataRequest(ITmfEvent.class, (int) rank, nbRequested, fTrace.getCacheSize(), ExecutionType.BACKGROUND) {
1365 long currentRank = rank;
1366
1367 @Override
1368 public void handleData(final ITmfEvent event) {
1369 super.handleData(event);
1370 if (searchFilter.matches(event) && ((eventFilter == null) || eventFilter.matches(event))) {
1371 foundRank = currentRank;
ae3ffd37 1372 foundTimestamp = event.getTimestamp();
db4721fb
PT
1373 if (direction == Direction.FORWARD) {
1374 done();
1375 return;
1376 }
1377 }
1378 currentRank++;
1379 }
1380 };
1381 ((ITmfDataProvider) fTrace).sendRequest(request);
1382 try {
1383 request.waitForCompletion();
1384 if (request.isCancelled()) {
1385 return Status.OK_STATUS;
1386 }
1387 } catch (final InterruptedException e) {
1388 synchronized (fSearchSyncObj) {
1389 fSearchThread = null;
1390 }
1391 return Status.OK_STATUS;
1392 }
1393 if (foundRank == -1) {
1394 if (direction == Direction.FORWARD) {
1395 if (rank == 0) {
1396 synchronized (fSearchSyncObj) {
1397 fSearchThread = null;
1398 }
1399 return Status.OK_STATUS;
1400 }
1401 nbRequested = (int) rank;
1402 rank = 0;
1403 wrapped = true;
1404 } else {
1405 rank--;
1406 if (rank < 0) {
1407 rank = (int) fTrace.getNbEvents() - 1;
1408 wrapped = true;
1409 }
1410 if ((rank <= startRank) && wrapped) {
1411 synchronized (fSearchSyncObj) {
1412 fSearchThread = null;
1413 }
1414 return Status.OK_STATUS;
1415 }
1416 }
1417 }
1418 }
1419 int index = (int) foundRank;
1420 if (eventFilter != null) {
1421 index = fCache.getFilteredEventIndex(foundRank);
1422 }
1423 final int selection = index + 1 + (eventFilter != null ? +1 : 0); // +1 for header row, +1 for top filter status row
1424
1425 display.asyncExec(new Runnable() {
1426 @Override
1427 public void run() {
1428 if (monitor.isCanceled()) {
1429 return;
1430 }
1431 if (fTable.isDisposed()) {
1432 return;
1433 }
1434 fTable.setSelection(selection);
1435 fSelectedRank = foundRank;
ae3ffd37
PT
1436 fRawViewer.selectAndReveal(fSelectedRank);
1437 if (foundTimestamp != null) {
1438 broadcast(new TmfTimeSynchSignal(TmfEventsTable.this, foundTimestamp));
1439 }
db4721fb
PT
1440 synchronized (fSearchSyncObj) {
1441 fSearchThread = null;
1442 }
1443 }
1444 });
1445 return Status.OK_STATUS;
1446 }
1447
1448 @Override
1449 protected void canceling() {
1450 request.cancel();
1451 synchronized (fSearchSyncObj) {
1452 fSearchThread = null;
1453 }
1454 }
1455 }
1456
1457 protected void createResources() {
1458 fGrayColor = fResourceManager.createColor(ColorUtil.blend(fTable.getBackground().getRGB(), fTable
1459 .getForeground().getRGB()));
1460 fGreenColor = fTable.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN);
1461 fBoldFont = fResourceManager.createFont(FontDescriptor.createFrom(fTable.getFont()).setStyle(SWT.BOLD));
1462 }
1463
1464 protected void packColumns() {
1465 if (fPackDone) {
1466 return;
1467 }
1468 for (final TableColumn column : fTable.getColumns()) {
1469 final int headerWidth = column.getWidth();
1470 column.pack();
1471 if (column.getWidth() < headerWidth) {
1472 column.setWidth(headerWidth);
1473 }
1474 }
1475 fPackDone = true;
1476 }
1477
1478 /**
1479 * @param event
1480 * @return
1481 *
1482 * FIXME: Add support for column selection
1483 */
1484 protected ITmfEventField[] extractItemFields(final ITmfEvent event) {
1485 ITmfEventField[] fields = new TmfEventField[0];
1486 if (event != null) {
1487 final String timestamp = event.getTimestamp().toString();
1488 final String source = event.getSource();
1489 final String type = event.getType().getName();
1490 final String reference = event.getReference();
a4a6acf4 1491 final String content = event.getContent().toString();
db4721fb
PT
1492 fields = new TmfEventField[] {
1493 new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp),
1494 new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source),
1495 new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type),
1496 new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference),
a4a6acf4 1497 new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, content)
db4721fb
PT
1498 };
1499 }
1500 return fields;
1501 }
1502
1503 /**
1504 * Notify this table that is got the UI focus.
1505 */
1506 public void setFocus() {
1507 fTable.setFocus();
1508 }
1509
1510 /**
1511 * Assign a new trace to this event table.
1512 *
1513 * @param trace
1514 * The trace to assign to this event table
1515 * @param disposeOnClose
1516 * true if the trace should be disposed when the table is
1517 * disposed
1518 */
1519 public void setTrace(final ITmfTrace trace, final boolean disposeOnClose) {
1520 if ((fTrace != null) && fDisposeOnClose) {
1521 fTrace.dispose();
1522 }
1523 fTrace = trace;
1524 fPackDone = false;
1525 fSelectedRank = 0;
1526 fDisposeOnClose = disposeOnClose;
1527
1528 // Perform the updates on the UI thread
1529 fTable.getDisplay().syncExec(new Runnable() {
1530 @Override
1531 public void run() {
1532 fTable.removeAll();
1533 fCache.setTrace(fTrace); // Clear the cache
1534 if (fTrace != null) {
1535 if (!fTable.isDisposed() && (fTrace != null)) {
1536 if (fTable.getData(Key.FILTER_OBJ) == null) {
1537 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1538 } else {
1539 stopFilterThread();
1540 fFilterMatchCount = 0;
1541 fFilterCheckCount = 0;
1542 fTable.setItemCount(3); // +1 for header row, +2 for top and bottom filter status rows
1543 startFilterThread();
1544 }
1545 }
db4721fb 1546 }
ea279a69 1547 fRawViewer.setTrace(fTrace);
db4721fb
PT
1548 }
1549 });
1550 }
1551
1552 // ------------------------------------------------------------------------
1553 // Event cache
1554 // ------------------------------------------------------------------------
1555
1556 /**
1557 * Notify that the event cache has been updated
1558 *
1559 * @param completed
1560 * Also notify if the populating of the cache is complete, or
1561 * not.
1562 */
1563 public void cacheUpdated(final boolean completed) {
1564 synchronized (fCacheUpdateSyncObj) {
1565 if (fCacheUpdateBusy) {
1566 fCacheUpdatePending = true;
1567 fCacheUpdateCompleted = completed;
1568 return;
1569 }
1570 fCacheUpdateBusy = true;
1571 }
1572 // Event cache is now updated. Perform update on the UI thread
1573 if (!fTable.isDisposed()) {
1574 fTable.getDisplay().asyncExec(new Runnable() {
1575 @Override
1576 public void run() {
1577 if (!fTable.isDisposed()) {
1578 fTable.refresh();
1579 packColumns();
1580 }
1581 if (completed) {
1582 populateCompleted();
1583 }
1584 synchronized (fCacheUpdateSyncObj) {
1585 fCacheUpdateBusy = false;
1586 if (fCacheUpdatePending) {
1587 fCacheUpdatePending = false;
1588 cacheUpdated(fCacheUpdateCompleted);
1589 }
1590 }
1591 }
1592 });
1593 }
1594 }
1595
1596 protected void populateCompleted() {
1597 // Nothing by default;
1598 }
1599
93bfd50a
PT
1600 // ------------------------------------------------------------------------
1601 // ISelectionProvider
1602 // ------------------------------------------------------------------------
1603
1604 /* (non-Javadoc)
1605 * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1606 */
1607 /**
1608 * @since 2.0
1609 */
1610 @Override
1611 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1612 selectionChangedListeners.add(listener);
1613 }
1614
1615 /* (non-Javadoc)
1616 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
1617 */
1618 /**
1619 * @since 2.0
1620 */
1621 @Override
1622 public ISelection getSelection() {
1623 if (fTable == null || fTable.isDisposed()) {
1624 return StructuredSelection.EMPTY;
1625 }
1626 List<Object> list = new ArrayList<Object>(fTable.getSelection().length);
1627 for (TableItem item : fTable.getSelection()) {
1628 if (item.getData() != null) {
1629 list.add(item.getData());
1630 }
1631 }
1632 return new StructuredSelection(list);
1633 }
1634
1635 /* (non-Javadoc)
1636 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1637 */
1638 /**
1639 * @since 2.0
1640 */
1641 @Override
1642 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
1643 selectionChangedListeners.remove(listener);
1644 }
1645
1646 /* (non-Javadoc)
1647 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
1648 */
1649 /**
1650 * @since 2.0
1651 */
1652 @Override
1653 public void setSelection(ISelection selection) {
1654 // not implemented
1655 }
1656
1657 /**
1658 * Notifies any selection changed listeners that the viewer's selection has changed.
1659 * Only listeners registered at the time this method is called are notified.
1660 *
1661 * @param event a selection changed event
1662 *
1663 * @see ISelectionChangedListener#selectionChanged
1664 * @since 2.0
1665 */
1666 protected void fireSelectionChanged(final SelectionChangedEvent event) {
1667 Object[] listeners = selectionChangedListeners.getListeners();
1668 for (int i = 0; i < listeners.length; ++i) {
1669 final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
1670 SafeRunnable.run(new SafeRunnable() {
faa38350 1671 @Override
93bfd50a
PT
1672 public void run() {
1673 l.selectionChanged(event);
1674 }
1675 });
1676 }
1677 }
1678
db4721fb
PT
1679 // ------------------------------------------------------------------------
1680 // Bookmark handling
1681 // ------------------------------------------------------------------------
1682
1683 /**
1684 * Add a bookmark to this event table.
1685 *
1686 * @param bookmarksFile
1687 * The file to use for the bookmarks
1688 */
1689 public void addBookmark(final IFile bookmarksFile) {
1690 fBookmarksFile = bookmarksFile;
1691 final TableItem[] selection = fTable.getSelection();
1692 if (selection.length > 0) {
1693 final TableItem tableItem = selection[0];
1694 if (tableItem.getData(Key.RANK) != null) {
1695 final StringBuffer defaultMessage = new StringBuffer();
1696 for (int i = 0; i < fTable.getColumns().length; i++) {
1697 if (i > 0)
1698 {
1699 defaultMessage.append(", "); //$NON-NLS-1$
1700 }
1701 defaultMessage.append(tableItem.getText(i));
1702 }
3be2946f
PT
1703 final InputDialog dialog = new MultiLineInputDialog(
1704 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
1705 Messages.TmfEventsTable_AddBookmarkDialogTitle,
1706 Messages.TmfEventsTable_AddBookmarkDialogMessage,
1707 defaultMessage.toString());
db4721fb
PT
1708 if (dialog.open() == Window.OK) {
1709 final String message = dialog.getValue();
1710 try {
1711 final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
1712 if (bookmark.exists()) {
1713 bookmark.setAttribute(IMarker.MESSAGE, message.toString());
1714 final long rank = (Long) tableItem.getData(Key.RANK);
1715 final int location = (int) rank;
1716 bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
1717 fBookmarksMap.put(rank, bookmark.getId());
1718 fTable.refresh();
1719 }
1720 } catch (final CoreException e) {
1721 displayException(e);
1722 }
1723 }
1724 }
1725 }
1726
1727 }
1728
1729 /**
1730 * Remove a bookmark from this event table.
1731 *
1732 * @param bookmark
1733 * The bookmark to remove
1734 */
1735 public void removeBookmark(final IMarker bookmark) {
1736 for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
1737 if (entry.getValue().equals(bookmark.getId())) {
1738 fBookmarksMap.remove(entry.getKey());
1739 fTable.refresh();
1740 return;
1741 }
1742 }
1743 }
1744
1745 private void toggleBookmark(final long rank) {
1746 if (fBookmarksFile == null) {
1747 return;
1748 }
1749 if (fBookmarksMap.containsKey(rank)) {
1750 final Long markerId = fBookmarksMap.remove(rank);
1751 fTable.refresh();
1752 try {
1753 final IMarker bookmark = fBookmarksFile.findMarker(markerId);
1754 if (bookmark != null) {
1755 bookmark.delete();
1756 }
1757 } catch (final CoreException e) {
1758 displayException(e);
1759 }
1760 } else {
1761 addBookmark(fBookmarksFile);
1762 }
1763 }
1764
1765 /**
1766 * Refresh the bookmarks assigned to this trace, from the contents of a
1767 * bookmark file.
1768 *
1769 * @param bookmarksFile
1770 * The bookmark file to use
1771 */
1772 public void refreshBookmarks(final IFile bookmarksFile) {
1773 fBookmarksFile = bookmarksFile;
1774 if (bookmarksFile == null) {
1775 fBookmarksMap.clear();
1776 fTable.refresh();
1777 return;
1778 }
1779 try {
1780 fBookmarksMap.clear();
1781 for (final IMarker bookmark : bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO)) {
1782 final int location = bookmark.getAttribute(IMarker.LOCATION, -1);
1783 if (location != -1) {
1784 final long rank = location;
1785 fBookmarksMap.put(rank, bookmark.getId());
1786 }
1787 }
1788 fTable.refresh();
1789 } catch (final CoreException e) {
1790 displayException(e);
1791 }
1792 }
1793
1794 @Override
1795 public void gotoMarker(final IMarker marker) {
1796 final int rank = marker.getAttribute(IMarker.LOCATION, -1);
1797 if (rank != -1) {
1798 int index = rank;
1799 if (fTable.getData(Key.FILTER_OBJ) != null) {
1800 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
1801 } else if (rank >= fTable.getItemCount()) {
1802 fPendingGotoRank = rank;
1803 }
1804 fTable.setSelection(index + 1); // +1 for header row
1805 }
1806 }
1807
1808 // ------------------------------------------------------------------------
1809 // Listeners
1810 // ------------------------------------------------------------------------
1811
1812 /*
1813 * (non-Javadoc)
1814 *
1815 * @see org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener#colorSettingsChanged(org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting[])
1816 */
1817 @Override
1818 public void colorSettingsChanged(final ColorSetting[] colorSettings) {
1819 fTable.refresh();
1820 }
1821
db4721fb
PT
1822 // ------------------------------------------------------------------------
1823 // Signal handlers
1824 // ------------------------------------------------------------------------
1825
db4721fb
PT
1826 /**
1827 * Handler for the trace updated signal
1828 *
1829 * @param signal
1830 * The incoming signal
1831 */
1832 @TmfSignalHandler
1833 public void traceUpdated(final TmfTraceUpdatedSignal signal) {
1834 if ((signal.getTrace() != fTrace) || fTable.isDisposed()) {
1835 return;
1836 }
1837 // Perform the refresh on the UI thread
1838 Display.getDefault().asyncExec(new Runnable() {
1839 @Override
1840 public void run() {
1841 if (!fTable.isDisposed() && (fTrace != null)) {
1842 if (fTable.getData(Key.FILTER_OBJ) == null) {
1843 fTable.setItemCount((int) fTrace.getNbEvents() + 1); // +1 for header row
1844 if ((fPendingGotoRank != -1) && ((fPendingGotoRank + 1) < fTable.getItemCount())) { // +1 for header row
1845 fTable.setSelection((int) fPendingGotoRank + 1); // +1 for header row
1846 fPendingGotoRank = -1;
1847 }
1848 } else {
1849 startFilterThread();
1850 }
1851 }
1852 if (!fRawViewer.isDisposed() && (fTrace != null)) {
1853 fRawViewer.refreshEventCount();
1854 }
1855 }
1856 });
1857 }
1858
1859 /**
1860 * Handler for the time synch signal.
1861 *
1862 * @param signal
1863 * The incoming signal
1864 */
1865 @TmfSignalHandler
1866 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
1867 if ((signal.getSource() != this) && (fTrace != null) && (!fTable.isDisposed())) {
1868
1869 // Create a request for one event that will be queued after other ongoing requests. When this request is completed
1870 // do the work to select the actual event with the timestamp specified in the signal. This procedure prevents
1871 // the method fTrace.getRank() from interfering and delaying ongoing requests.
1872 final TmfDataRequest subRequest = new TmfDataRequest(ITmfEvent.class, 0, 1, ExecutionType.FOREGROUND) {
1873
1874 TmfTimestamp ts = new TmfTimestamp(signal.getCurrentTime());
1875
1876 @Override
1877 public void handleData(final ITmfEvent event) {
1878 super.handleData(event);
1879 }
1880
1881 @Override
1882 public void handleCompleted() {
1883 super.handleCompleted();
1884 if (fTrace == null) {
1885 return;
1886 }
1887
1888 // Verify if the event is within the trace range and adjust if necessary
1889 ITmfTimestamp timestamp = ts;
1890 if (timestamp.compareTo(fTrace.getStartTime(), true) == -1) {
1891 timestamp = fTrace.getStartTime();
1892 }
1893 if (timestamp.compareTo(fTrace.getEndTime(), true) == 1) {
1894 timestamp = fTrace.getEndTime();
1895 }
1896
1897 // Get the rank of the selected event in the table
1898 final ITmfContext context = fTrace.seekEvent(timestamp);
1899 final long rank = context.getRank();
4c9f2944 1900 context.dispose();
db4721fb
PT
1901 fSelectedRank = rank;
1902
1903 fTable.getDisplay().asyncExec(new Runnable() {
1904 @Override
1905 public void run() {
1906 // Return if table is disposed
1907 if (fTable.isDisposed()) {
1908 return;
1909 }
1910
1911 int index = (int) rank;
1912 if (fTable.isDisposed()) {
1913 return;
1914 }
1915 if (fTable.getData(Key.FILTER_OBJ) != null)
1916 {
1917 index = fCache.getFilteredEventIndex(rank) + 1; // +1 for top filter status row
1918 }
1919 fTable.setSelection(index + 1); // +1 for header row
1920 fRawViewer.selectAndReveal(rank);
1921 }
1922 });
1923 }
1924 };
1925
1926 ((ITmfDataProvider) fTrace).sendRequest(subRequest);
1927 }
1928 }
1929
1930 // ------------------------------------------------------------------------
1931 // Error handling
1932 // ------------------------------------------------------------------------
1933
1934 /**
1935 * Display an exception in a message box
1936 *
1937 * @param e the exception
1938 */
1939 private static void displayException(final Exception e) {
1940 final MessageBox mb = new MessageBox(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
1941 mb.setText(e.getClass().getName());
1942 mb.setMessage(e.getMessage());
1943 mb.open();
1944 }
1945
f8177ba2
FC
1946 /**
1947 * @since 2.0
1948 */
1949 public void refresh() {
1950 fCache.clear();
1951 fTable.refresh();
1952 fTable.redraw();
1953 }
db4721fb 1954}
This page took 0.110125 seconds and 5 git commands to generate.