tmf: Bug 479994: TmfTraceSelectedSignal not sent when editor reused
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / editors / TmfEventsEditor.java
1 /*******************************************************************************
2 * Copyright (c) 2010, 2015 Ericsson, École Polytechnique de Montréal
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 * Patrick Tasse - Initial API and implementation
11 * Geneviève Bastien - Experiment instantiated with experiment type
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.ui.editors;
15
16 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
18 import java.util.List;
19
20 import org.eclipse.core.resources.IFile;
21 import org.eclipse.core.resources.IMarker;
22 import org.eclipse.core.resources.IMarkerDelta;
23 import org.eclipse.core.resources.IResource;
24 import org.eclipse.core.resources.IResourceChangeEvent;
25 import org.eclipse.core.resources.IResourceChangeListener;
26 import org.eclipse.core.resources.IResourceDelta;
27 import org.eclipse.core.resources.ResourcesPlugin;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.InvalidRegistryObjectException;
31 import org.eclipse.core.runtime.ListenerList;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.eclipse.jface.action.IStatusLineManager;
35 import org.eclipse.jface.util.SafeRunnable;
36 import org.eclipse.jface.viewers.ISelection;
37 import org.eclipse.jface.viewers.ISelectionChangedListener;
38 import org.eclipse.jface.viewers.ISelectionProvider;
39 import org.eclipse.jface.viewers.SelectionChangedEvent;
40 import org.eclipse.jface.viewers.StructuredSelection;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.Display;
43 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
44 import org.eclipse.tracecompass.internal.tmf.ui.editors.ITmfEventsEditorConstants;
45 import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
46 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
47 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
48 import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal;
49 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
50 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
51 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
52 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
53 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
54 import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
55 import org.eclipse.tracecompass.tmf.ui.project.model.Messages;
56 import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentElement;
57 import org.eclipse.tracecompass.tmf.ui.project.model.TmfOpenTraceHelper;
58 import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectElement;
59 import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectRegistry;
60 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
61 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceTypeUIUtils;
62 import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsTable;
63 import org.eclipse.ui.IEditorInput;
64 import org.eclipse.ui.IEditorPart;
65 import org.eclipse.ui.IEditorSite;
66 import org.eclipse.ui.IFileEditorInput;
67 import org.eclipse.ui.IPartListener;
68 import org.eclipse.ui.IPropertyListener;
69 import org.eclipse.ui.IReusableEditor;
70 import org.eclipse.ui.IWorkbenchPart;
71 import org.eclipse.ui.PartInitException;
72 import org.eclipse.ui.PlatformUI;
73 import org.eclipse.ui.ide.IGotoMarker;
74 import org.eclipse.ui.part.FileEditorInput;
75 import org.eclipse.ui.views.properties.IPropertySheetPage;
76
77 import com.google.common.collect.ImmutableSet;
78 import com.google.common.collect.Iterables;
79
80 /**
81 * Editor for TMF events
82 *
83 * @author Patrick Tasse
84 */
85 public class TmfEventsEditor extends TmfEditor implements ITmfTraceEditor, IReusableEditor, IPropertyListener, IResourceChangeListener, ISelectionProvider, ISelectionChangedListener, IPartListener, IGotoMarker {
86
87 /** ID for this class */
88 public static final String ID = "org.eclipse.linuxtools.tmf.ui.editors.events"; //$NON-NLS-1$
89
90 private TmfEventsTable fEventsTable;
91 private IFile fFile;
92 private ITmfTrace fTrace;
93 private Composite fParent;
94 private ListenerList fSelectionChangedListeners = new ListenerList();
95 private boolean fTraceSelected;
96 private IMarker fPendingGotoMarker;
97
98 @Override
99 public void doSave(final IProgressMonitor monitor) {
100 }
101
102 @Override
103 public void doSaveAs() {
104 }
105
106 @Override
107 public void init(final IEditorSite site, IEditorInput input) throws PartInitException {
108 IFileEditorInput fileEditorInput;
109 if (input instanceof TmfEditorInput) {
110 fFile = ((TmfEditorInput) input).getFile();
111 fTrace = ((TmfEditorInput) input).getTrace();
112 /* change the input to a FileEditorInput to allow open handlers to find this editor */
113 fileEditorInput = new FileEditorInput(fFile);
114 } else if (input instanceof IFileEditorInput) {
115 fileEditorInput = (IFileEditorInput) input;
116 fFile = fileEditorInput.getFile();
117 if (fFile == null) {
118 throw new PartInitException("Invalid IFileEditorInput: " + fileEditorInput); //$NON-NLS-1$
119 }
120 try {
121 final String traceTypeId = fFile.getPersistentProperty(TmfCommonConstants.TRACETYPE);
122 if (traceTypeId == null) {
123 throw new PartInitException(Messages.TmfOpenTraceHelper_NoTraceType);
124 }
125 if (ITmfEventsEditorConstants.EXPERIMENT_INPUT_TYPE_CONSTANTS.contains(traceTypeId)) {
126 // Special case: experiment bookmark resource
127 final TmfProjectElement project = TmfProjectRegistry.getProject(fFile.getProject(), true);
128 if (project == null) {
129 throw new PartInitException(Messages.TmfOpenTraceHelper_NoTraceType);
130 }
131 for (final TmfExperimentElement experimentElement : project.getExperimentsFolder().getExperiments()) {
132 if (experimentElement.getResource().equals(fFile.getParent())) {
133 setPartName(experimentElement.getName());
134 super.setSite(site);
135 super.setInput(fileEditorInput);
136 TmfOpenTraceHelper.reopenTraceFromElement(experimentElement, this);
137 return;
138 }
139 }
140 } else if (ITmfEventsEditorConstants.TRACE_INPUT_TYPE_CONSTANTS.contains(traceTypeId)) {
141 // Special case: trace bookmark resource
142 final TmfProjectElement project = TmfProjectRegistry.getProject(fFile.getProject(), true);
143 for (final TmfTraceElement traceElement : project.getTracesFolder().getTraces()) {
144 if (traceElement.getResource().equals(fFile.getParent())) {
145 setPartName(traceElement.getElementPath());
146 super.setSite(site);
147 super.setInput(fileEditorInput);
148 TmfOpenTraceHelper.reopenTraceFromElement(traceElement, this);
149 return;
150 }
151 }
152 } else {
153 final TmfProjectElement project = TmfProjectRegistry.getProject(fFile.getProject(), true);
154 for (final TmfTraceElement traceElement : project.getTracesFolder().getTraces()) {
155 if (traceElement.getResource().equals(fFile)) {
156 setPartName(traceElement.getElementPath());
157 super.setSite(site);
158 super.setInput(fileEditorInput);
159 TmfOpenTraceHelper.reopenTraceFromElement(traceElement, this);
160 return;
161 }
162 }
163 }
164 } catch (final PartInitException e) {
165 throw e;
166 } catch (final InvalidRegistryObjectException e) {
167 Activator.getDefault().logError("Error initializing TmfEventsEditor", e); //$NON-NLS-1$
168 } catch (final CoreException e) {
169 Activator.getDefault().logError("Error initializing TmfEventsEditor", e); //$NON-NLS-1$
170 }
171 } else {
172 throw new PartInitException("Invalid IEditorInput: " + input.getClass()); //$NON-NLS-1$
173 }
174 if (fTrace == null) {
175 throw new PartInitException("Invalid IEditorInput: " + fFile.getName()); //$NON-NLS-1$
176 }
177 super.setSite(site);
178 super.setInput(fileEditorInput);
179 }
180
181 @Override
182 public boolean isDirty() {
183 return false;
184 }
185
186 @Override
187 public boolean isSaveAsAllowed() {
188 return false;
189 }
190
191 @Override
192 public void setInput(final IEditorInput input) {
193 super.setInput(input);
194 firePropertyChange(IEditorPart.PROP_INPUT);
195 }
196
197 @Override
198 public void propertyChanged(final Object source, final int propId) {
199 if (propId == IEditorPart.PROP_INPUT && getEditorInput() instanceof TmfEditorInput) {
200 if (fTrace != null) {
201 broadcast(new TmfTraceClosedSignal(this, fTrace));
202 TmfTraceColumnManager.saveColumnOrder(fTrace.getTraceTypeId(), fEventsTable.getColumnOrder());
203 }
204 fEventsTable.dispose();
205 fFile = ((TmfEditorInput) getEditorInput()).getFile();
206 fTrace = ((TmfEditorInput) getEditorInput()).getTrace();
207 /* change the input to a FileEditorInput to allow open handlers to find this editor */
208 super.setInput(new FileEditorInput(fFile));
209 createAndInitializeTable();
210 // The table was swapped for a new one, make sure it gets focus if
211 // the editor is active. Otherwise, the new table will not get focus
212 // because the editor already had focus.
213 if (!PlatformUI.getWorkbench().isClosing() && PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart() == getSite().getPart()) {
214 fEventsTable.setFocus();
215 }
216 fParent.layout();
217 }
218 }
219
220 @Override
221 public void createPartControl(final Composite parent) {
222 fParent = parent;
223 createAndInitializeTable();
224 addPropertyListener(this);
225 ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
226 // we need to wrap the ISelectionProvider interface in the editor because
227 // the events table can be replaced later while the selection changed listener
228 // is only added once by the platform to the selection provider set here
229 getSite().setSelectionProvider(this);
230 getSite().getPage().addPartListener(this);
231 }
232
233 private void createAndInitializeTable() {
234 if (fTrace != null) {
235 setPartName(fTrace.getName());
236 fEventsTable = createEventsTable(fParent, fTrace.getCacheSize());
237 fEventsTable.registerContextMenus(getSite());
238 fEventsTable.setColumnOrder(TmfTraceColumnManager.loadColumnOrder(fTrace.getTraceTypeId()));
239 fEventsTable.addSelectionChangedListener(this);
240 fEventsTable.setTrace(fTrace, true);
241 fEventsTable.refreshBookmarks(fFile);
242 if (fPendingGotoMarker != null) {
243 fEventsTable.gotoMarker(fPendingGotoMarker);
244 fPendingGotoMarker = null;
245 }
246
247 /* ensure start time is set */
248 final ITmfContext context = fTrace.seekEvent(0);
249 fTrace.getNext(context);
250 context.dispose();
251
252 broadcast(new TmfTraceOpenedSignal(this, fTrace, fFile));
253 if (fTraceSelected) {
254 broadcast(new TmfTraceSelectedSignal(this, fTrace));
255 }
256 } else {
257 fEventsTable = new TmfEventsTable(fParent, 0);
258 fEventsTable.addSelectionChangedListener(this);
259 }
260 IStatusLineManager statusLineManager = getEditorSite().getActionBars().getStatusLineManager();
261 fEventsTable.setStatusLineManager(statusLineManager);
262 }
263
264 @Override
265 public void dispose() {
266 if (getSite() != null) {
267 getSite().getPage().removePartListener(this);
268 }
269 ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
270 removePropertyListener(this);
271 if (fTrace != null) {
272 broadcast(new TmfTraceClosedSignal(this, fTrace));
273 if (fEventsTable != null) {
274 TmfTraceColumnManager.saveColumnOrder(fTrace.getTraceTypeId(), fEventsTable.getColumnOrder());
275 }
276 }
277 if (fEventsTable != null) {
278 fEventsTable.dispose();
279 }
280 super.dispose();
281 }
282
283 /**
284 * Create the event table
285 *
286 * @param parent
287 * The parent composite
288 * @param cacheSize
289 * The cache size
290 * @return The event table instance
291 */
292 protected @NonNull TmfEventsTable createEventsTable(final Composite parent, final int cacheSize) {
293 ITmfTrace trace = fTrace;
294
295 /*
296 * Check if the trace (or experiment type) defines a specific event
297 * table in its extension point.
298 */
299 TmfEventsTable table = TmfTraceTypeUIUtils.getEventTable(trace, parent, cacheSize);
300 if (table != null) {
301 return table;
302 }
303
304 /*
305 * Use the aspects defined by the trace type (or each trace type in an
306 * experiment) to build a table consisting of these.
307 */
308 Iterable<ITmfEventAspect> aspects = getTraceAspects(trace);
309 if (Iterables.isEmpty(aspects)) {
310 /* Couldn't find any event aspects, use a default table */
311 return new TmfEventsTable(parent, cacheSize);
312 }
313 return new TmfEventsTable(parent, cacheSize, aspects);
314 }
315
316 /**
317 * Get the event table for the given trace. It will be of the type defined
318 * by the extension point if applicable, else it will be a default table
319 * with the extension-point-defined columns (if any).
320 *
321 * @param trace
322 * The event table is for this trace
323 * @param parent
324 * The parent composite of the table
325 * @param cacheSize
326 * The cache size to use
327 * @return The event table for the trace
328 */
329 private static @NonNull Iterable<ITmfEventAspect> getTraceAspects(ITmfTrace trace) {
330 if (trace instanceof TmfExperiment) {
331 return getExperimentAspects((TmfExperiment) trace);
332 }
333 return trace.getEventAspects();
334 }
335
336 /**
337 * Get the events table for an experiment. If all traces in the experiment
338 * are of the same type, use the same behavior as if it was one trace of
339 * that type.
340 *
341 * @param experiment
342 * the experiment
343 * @param parent
344 * the parent Composite
345 * @param cacheSize
346 * the event table cache size
347 * @return An event table of the appropriate type
348 */
349 private static @NonNull Iterable<ITmfEventAspect> getExperimentAspects(
350 final TmfExperiment experiment) {
351 List<ITmfTrace> traces = experiment.getTraces();
352 ImmutableSet.Builder<ITmfEventAspect> builder = new ImmutableSet.Builder<>();
353
354 /* For experiments, we'll add a "trace name" aspect/column */
355 builder.add(ITmfEventAspect.BaseAspects.TRACE_NAME);
356
357 String commonTraceType = getCommonTraceType(experiment);
358 if (commonTraceType != null) {
359 /*
360 * All the traces in this experiment are of the same type, let's
361 * just use the normal table for that type.
362 */
363 builder.addAll(traces.get(0).getEventAspects());
364
365 } else {
366 /*
367 * There are different trace types in the experiment, so we are
368 * definitely using a TmfEventsTable. Aggregate the columns from all
369 * trace types.
370 */
371 for (ITmfTrace trace : traces) {
372 Iterable<ITmfEventAspect> traceAspects = trace.getEventAspects();
373 builder.addAll(traceAspects);
374 }
375 }
376 return checkNotNull(builder.build());
377 }
378
379 /**
380 * Check if an experiment contains traces of all the same type. If so,
381 * returns this type as a String. If not, returns null.
382 *
383 * @param experiment
384 * The experiment
385 * @return The common trace type if there is one, or 'null' if there are
386 * different types.
387 */
388 private static @Nullable String getCommonTraceType(TmfExperiment experiment) {
389 String commonTraceType = null;
390 try {
391 for (final ITmfTrace trace : experiment.getTraces()) {
392 final IResource resource = trace.getResource();
393 if (resource == null) {
394 return null;
395 }
396
397 final String traceType = resource.getPersistentProperty(TmfCommonConstants.TRACETYPE);
398 if ((commonTraceType != null) && !commonTraceType.equals(traceType)) {
399 return null;
400 }
401 commonTraceType = traceType;
402 }
403 } catch (CoreException e) {
404 /*
405 * One of the traces didn't advertise its type, we can't infer
406 * anything.
407 */
408 return null;
409 }
410 return commonTraceType;
411 }
412
413 @Override
414 public ITmfTrace getTrace() {
415 return fTrace;
416 }
417
418 @Override
419 public void setFocus() {
420 fEventsTable.setFocus();
421 }
422
423 @Override
424 public <T> T getAdapter(final Class<T> adapter) {
425 if (IGotoMarker.class.equals(adapter)) {
426 if (fTrace == null || fEventsTable == null) {
427 return adapter.cast(this);
428 }
429 return adapter.cast(fEventsTable);
430 } else if (IPropertySheetPage.class.equals(adapter)) {
431 return adapter.cast(new UnsortedPropertySheetPage());
432 }
433 return super.getAdapter(adapter);
434 }
435
436 @Override
437 public void gotoMarker(IMarker marker) {
438 if (fTrace == null || fEventsTable == null) {
439 fPendingGotoMarker = marker;
440 } else {
441 fEventsTable.gotoMarker(marker);
442 }
443 }
444
445 @Override
446 public void resourceChanged(final IResourceChangeEvent event) {
447 for (final IMarkerDelta delta : event.findMarkerDeltas(IMarker.BOOKMARK, false)) {
448 if (delta.getResource().equals(fFile)) {
449 if (delta.getKind() == IResourceDelta.REMOVED) {
450 final IMarker bookmark = delta.getMarker();
451 Display.getDefault().asyncExec(new Runnable() {
452 @Override
453 public void run() {
454 fEventsTable.removeBookmark(bookmark);
455 }
456 });
457 } else if (delta.getKind() == IResourceDelta.CHANGED) {
458 Display.getDefault().asyncExec(new Runnable() {
459 @Override
460 public void run() {
461 fEventsTable.getTable().refresh();
462 }
463 });
464 }
465 }
466 }
467 }
468
469 // ------------------------------------------------------------------------
470 // ISelectionProvider
471 // ------------------------------------------------------------------------
472
473 @Override
474 public void addSelectionChangedListener(ISelectionChangedListener listener) {
475 fSelectionChangedListeners.add(listener);
476 }
477
478 @Override
479 public ISelection getSelection() {
480 if (fEventsTable == null) {
481 return StructuredSelection.EMPTY;
482 }
483 return fEventsTable.getSelection();
484 }
485
486 @Override
487 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
488 fSelectionChangedListeners.remove(listener);
489 }
490
491 @Override
492 public void setSelection(ISelection selection) {
493 // not implemented
494 }
495
496 /**
497 * Notifies any selection changed listeners that the viewer's selection has changed.
498 * Only listeners registered at the time this method is called are notified.
499 *
500 * @param event a selection changed event
501 *
502 * @see ISelectionChangedListener#selectionChanged
503 */
504 protected void fireSelectionChanged(final SelectionChangedEvent event) {
505 Object[] listeners = fSelectionChangedListeners.getListeners();
506 for (int i = 0; i < listeners.length; ++i) {
507 final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
508 SafeRunnable.run(new SafeRunnable() {
509 @Override
510 public void run() {
511 l.selectionChanged(event);
512 }
513 });
514 }
515 }
516
517 // ------------------------------------------------------------------------
518 // ISelectionChangedListener
519 // ------------------------------------------------------------------------
520
521 @Override
522 public void selectionChanged(SelectionChangedEvent event) {
523 fireSelectionChanged(event);
524 }
525
526 // ------------------------------------------------------------------------
527 // IPartListener
528 // ------------------------------------------------------------------------
529
530 @Override
531 public void partActivated(IWorkbenchPart part) {
532 if (part == this && !fTraceSelected) {
533 fTraceSelected = true;
534 if (fTrace == null) {
535 return;
536 }
537 broadcast(new TmfTraceSelectedSignal(this, fTrace));
538 }
539 }
540
541 @Override
542 public void partBroughtToTop(IWorkbenchPart part) {
543 if (part == this && !fTraceSelected) {
544 fTraceSelected = true;
545 if (fTrace == null) {
546 return;
547 }
548 broadcast(new TmfTraceSelectedSignal(this, fTrace));
549 }
550 }
551
552 @Override
553 public void partClosed(IWorkbenchPart part) {
554 }
555
556 @Override
557 public void partDeactivated(IWorkbenchPart part) {
558 }
559
560 @Override
561 public void partOpened(IWorkbenchPart part) {
562 }
563
564 // ------------------------------------------------------------------------
565 // Global commands
566 // ------------------------------------------------------------------------
567
568 /**
569 * Add a bookmark
570 */
571 public void addBookmark() {
572 fEventsTable.addBookmark(fFile);
573 }
574
575
576 // ------------------------------------------------------------------------
577 // Signal handlers
578 // ------------------------------------------------------------------------
579
580 /**
581 * Handler for the Trace Selected signal
582 *
583 * @param signal The incoming signal
584 */
585 @TmfSignalHandler
586 public void traceSelected(final TmfTraceSelectedSignal signal) {
587 if ((signal.getSource() != this)) {
588 if (signal.getTrace().equals(fTrace)) {
589 getSite().getPage().bringToTop(this);
590 } else {
591 fTraceSelected = false;
592 }
593 }
594 }
595
596 /**
597 * Update the display to use the updated timestamp format
598 *
599 * @param signal the incoming signal
600 */
601 @TmfSignalHandler
602 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
603 if (fEventsTable != null) {
604 fEventsTable.refresh();
605 }
606 }
607
608 }
This page took 0.045688 seconds and 6 git commands to generate.