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