1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
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
10 * Matthew Khouzam - Initial API and implementation
11 * Patrick Tasse - Initial API and implementation
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.dialogs
.offset
;
16 import java
.text
.ParseException
;
17 import java
.util
.ArrayList
;
18 import java
.util
.Collections
;
19 import java
.util
.Comparator
;
20 import java
.util
.HashMap
;
21 import java
.util
.List
;
24 import org
.eclipse
.core
.runtime
.IPath
;
25 import org
.eclipse
.core
.runtime
.Path
;
26 import org
.eclipse
.jface
.dialogs
.Dialog
;
27 import org
.eclipse
.jface
.viewers
.CellEditor
;
28 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
29 import org
.eclipse
.jface
.viewers
.ColumnViewer
;
30 import org
.eclipse
.jface
.viewers
.ColumnViewerEditor
;
31 import org
.eclipse
.jface
.viewers
.ColumnViewerEditorActivationStrategy
;
32 import org
.eclipse
.jface
.viewers
.EditingSupport
;
33 import org
.eclipse
.jface
.viewers
.FocusCellOwnerDrawHighlighter
;
34 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
35 import org
.eclipse
.jface
.viewers
.TreeViewerColumn
;
36 import org
.eclipse
.jface
.viewers
.TreeViewerEditor
;
37 import org
.eclipse
.jface
.viewers
.TreeViewerFocusCellManager
;
38 import org
.eclipse
.jface
.viewers
.Viewer
;
39 import org
.eclipse
.swt
.SWT
;
40 import org
.eclipse
.swt
.custom
.TreeEditor
;
41 import org
.eclipse
.swt
.events
.DisposeEvent
;
42 import org
.eclipse
.swt
.events
.DisposeListener
;
43 import org
.eclipse
.swt
.events
.SelectionAdapter
;
44 import org
.eclipse
.swt
.events
.SelectionEvent
;
45 import org
.eclipse
.swt
.layout
.GridData
;
46 import org
.eclipse
.swt
.layout
.GridLayout
;
47 import org
.eclipse
.swt
.layout
.RowLayout
;
48 import org
.eclipse
.swt
.widgets
.Button
;
49 import org
.eclipse
.swt
.widgets
.Composite
;
50 import org
.eclipse
.swt
.widgets
.Control
;
51 import org
.eclipse
.swt
.widgets
.Display
;
52 import org
.eclipse
.swt
.widgets
.Group
;
53 import org
.eclipse
.swt
.widgets
.Label
;
54 import org
.eclipse
.swt
.widgets
.Shell
;
55 import org
.eclipse
.swt
.widgets
.Tree
;
56 import org
.eclipse
.swt
.widgets
.TreeColumn
;
57 import org
.eclipse
.swt
.widgets
.TreeItem
;
58 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfEventSelectedSignal
;
59 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
60 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
61 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSelectionRangeUpdatedSignal
;
62 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
63 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.ITmfTimestamp
;
64 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfNanoTimestamp
;
65 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestampFormat
;
66 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
67 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
68 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfOpenTraceHelper
;
69 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceElement
;
70 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.ArrayTreeContentProvider
;
71 import org
.eclipse
.ui
.dialogs
.FilteredTree
;
72 import org
.eclipse
.ui
.dialogs
.PatternFilter
;
75 * Offset wizard dialog
77 * @author Matthew Khouzam
80 public class OffsetDialog
extends Dialog
{
82 private static final int TREE_EDITOR_MIN_WIDTH
= 50;
83 private static final String EDITOR_KEY
= "$editor$"; //$NON-NLS-1$
84 private static final String WIDTH_KEY
= "$width$"; //$NON-NLS-1$
86 private static final TmfTimestampFormat TIME_FORMAT
= new TmfTimestampFormat("yyyy-MM-dd HH:mm:ss.SSS SSS SSS"); //$NON-NLS-1$
87 private static final TmfTimestampFormat OFFSET_FORMAT
= new TmfTimestampFormat("T.SSS SSS SSS"); //$NON-NLS-1$
89 private final Map
<TmfTraceElement
, Long
> fOffsetMap
;
90 private final Map
<TmfTraceElement
, ITmfTimestamp
> fRefTimeMap
;
91 private final Map
<TmfTraceElement
, ITmfTimestamp
> fTargetTimeMap
;
93 private Label fBasicMessageLabel
;
94 private Group fButtonGroup
;
95 private Label fAdvancedMessageLabel
;
96 private FilteredTree fViewer
;
98 private boolean fAdvancedMode
= true;
99 private TreeViewerColumn fButtonViewerColumn
;
100 private TreeColumn fRefTimeColumn
;
101 private TreeColumn fTargetTimeColumn
;
103 private abstract class ColumnEditingSupport
extends EditingSupport
{
104 private final TextCellEditor textCellEditor
;
106 private ColumnEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
108 this.textCellEditor
= textCellEditor
;
112 protected CellEditor
getCellEditor(Object element
) {
113 return textCellEditor
;
117 protected boolean canEdit(Object element
) {
122 private class TimeEditingSupport
extends ColumnEditingSupport
{
123 private Map
<TmfTraceElement
, ITmfTimestamp
> map
;
125 private TimeEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
, Map
<TmfTraceElement
, ITmfTimestamp
> map
) {
126 super(viewer
, textCellEditor
);
131 protected void setValue(Object element
, Object value
) {
132 if (value
instanceof String
) {
133 String string
= (String
) value
;
134 if (string
.trim().isEmpty()) {
138 ITmfTimestamp refTime
= map
.get(element
);
139 long ref
= refTime
== null ?
0 : refTime
.toNanos();
140 Long newVal
= TIME_FORMAT
.parseValue(string
, ref
);
141 map
.put((TmfTraceElement
) element
, new TmfNanoTimestamp(newVal
));
142 } catch (ParseException e
) {
143 /* Ignore and reload previous value */
146 fViewer
.getViewer().update(element
, null);
151 protected Object
getValue(Object element
) {
152 ITmfTimestamp ts
= map
.get(element
);
154 return ""; //$NON-NLS-1$
156 return TIME_FORMAT
.format(ts
.toNanos());
160 private class RefTimeEditingSupport
extends TimeEditingSupport
{
161 private RefTimeEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
162 super(viewer
, textCellEditor
, fRefTimeMap
);
166 private class TargetTimeEditingSupport
extends TimeEditingSupport
{
167 private TargetTimeEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
168 super(viewer
, textCellEditor
, fTargetTimeMap
);
172 private class OffsetEditingSupport
extends ColumnEditingSupport
{
173 private OffsetEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
174 super(viewer
, textCellEditor
);
178 protected void setValue(Object element
, Object value
) {
179 if (value
instanceof String
) {
180 String string
= (String
) value
;
181 if (string
.trim().isEmpty()) {
182 fOffsetMap
.put((TmfTraceElement
) element
, 0L);
185 Long newVal
= OFFSET_FORMAT
.parseValue(string
);
186 fOffsetMap
.put((TmfTraceElement
) element
, newVal
);
187 } catch (ParseException e
) {
188 /* Ignore and reload previous value */
191 fViewer
.getViewer().update(element
, null);
196 protected Object
getValue(Object element
) {
197 Long offset
= fOffsetMap
.get(element
);
198 if (offset
== null || offset
== 0) {
199 return ""; //$NON-NLS-1$
201 return OFFSET_FORMAT
.format(offset
.longValue());
211 * results to put the data into
213 public OffsetDialog(Shell parent
, Map
<TmfTraceElement
, Long
> results
) {
215 setShellStyle(getShellStyle() & ~SWT
.APPLICATION_MODAL
);
216 fOffsetMap
= results
;
217 fRefTimeMap
= new HashMap
<>();
218 fTargetTimeMap
= new HashMap
<>();
222 protected boolean isResizable() {
227 protected Control
createDialogArea(Composite parent
) {
228 getShell().setText(Messages
.OffsetDialog_Title
);
229 Composite area
= (Composite
) super.createDialogArea(parent
);
230 Composite composite
= new Composite(area
, SWT
.NONE
);
231 composite
.setLayoutData(new GridData(GridData
.FILL_BOTH
));
232 GridLayout gl
= new GridLayout();
235 composite
.setLayout(new GridLayout());
236 createBasicMessage(composite
);
237 createButtonGroup(composite
);
238 createAdvancedMessage(composite
);
239 createViewer(composite
);
241 /* set label width hint equal to tree width */
242 int widthHint
= fViewer
.getViewer().getTree().computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
).x
;
243 GridData gd
= (GridData
) fBasicMessageLabel
.getLayoutData();
244 gd
.widthHint
= widthHint
;
245 gd
= (GridData
) fAdvancedMessageLabel
.getLayoutData();
246 gd
.widthHint
= widthHint
;
247 gd
= (GridData
) composite
.getLayoutData();
248 gd
.heightHint
= composite
.computeSize(widthHint
, SWT
.DEFAULT
).y
;
251 TmfSignalManager
.register(this);
252 composite
.addDisposeListener(new DisposeListener() {
254 public void widgetDisposed(DisposeEvent e
) {
255 TmfSignalManager
.deregister(this);
261 private void createBasicMessage(final Composite parent
) {
262 fBasicMessageLabel
= new Label(parent
, SWT
.WRAP
);
263 fBasicMessageLabel
.setText(Messages
.OffsetDialog_BasicMessage
);
264 GridData gd
= new GridData(SWT
.FILL
, SWT
.CENTER
, true, false);
266 gd
.heightHint
= SWT
.DEFAULT
;
267 fBasicMessageLabel
.setLayoutData(gd
);
270 private void createButtonGroup(final Composite parent
) {
271 fButtonGroup
= new Group(parent
, SWT
.SHADOW_NONE
);
272 fButtonGroup
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, false, false));
273 fButtonGroup
.setLayout(new RowLayout(SWT
.HORIZONTAL
));
275 final Button basicButton
= new Button(fButtonGroup
, SWT
.RADIO
);
276 basicButton
.setText(Messages
.OffsetDialog_BasicButton
);
277 basicButton
.addSelectionListener(new SelectionAdapter() {
279 public void widgetSelected(SelectionEvent e
) {
280 if (!basicButton
.getSelection() || !fAdvancedMode
) {
287 basicButton
.setSelection(true);
289 final Button advancedButton
= new Button(fButtonGroup
, SWT
.RADIO
);
290 advancedButton
.setText(Messages
.OffsetDialog_AdvancedButton
);
291 advancedButton
.addSelectionListener(new SelectionAdapter() {
293 public void widgetSelected(SelectionEvent e
) {
294 if (!advancedButton
.getSelection() || fAdvancedMode
) {
303 private void createAdvancedMessage(final Composite parent
) {
304 fAdvancedMessageLabel
= new Label(parent
, SWT
.WRAP
);
305 fAdvancedMessageLabel
.setText(Messages
.OffsetDialog_AdvancedMessage
);
306 GridData gd
= new GridData(SWT
.FILL
, SWT
.CENTER
, true, false);
308 gd
.heightHint
= SWT
.DEFAULT
;
309 fAdvancedMessageLabel
.setLayoutData(gd
);
312 private void createViewer(Composite parent
) {
314 // Define the TableViewer
315 fViewer
= new FilteredTree(parent
, SWT
.MULTI
| SWT
.H_SCROLL
316 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
, new PatternFilter() {
318 protected boolean isLeafMatch(Viewer viewer
, Object element
) {
319 return wordMatches(((TmfTraceElement
) element
).getElementPath());
323 // Make lines and make header visible
324 final Tree tree
= fViewer
.getViewer().getTree();
325 tree
.setHeaderVisible(true);
326 tree
.setLinesVisible(true);
328 TreeViewerFocusCellManager focusCellManager
= new TreeViewerFocusCellManager(fViewer
.getViewer(), new FocusCellOwnerDrawHighlighter(fViewer
.getViewer()));
329 ColumnViewerEditorActivationStrategy actSupport
= new ColumnViewerEditorActivationStrategy(fViewer
.getViewer());
330 TreeViewerEditor
.create(fViewer
.getViewer(), focusCellManager
, actSupport
, ColumnViewerEditor
.TABBING_HORIZONTAL
331 | ColumnViewerEditor
.TABBING_MOVE_TO_ROW_NEIGHBOR
332 | ColumnViewerEditor
.TABBING_VERTICAL
| ColumnViewerEditor
.KEYBOARD_ACTIVATION
);
334 final TextCellEditor textCellEditor
= new TextCellEditor(fViewer
.getViewer().getTree(), SWT
.RIGHT
);
336 fViewer
.getViewer().setColumnProperties(new String
[] { Messages
.OffsetDialog_TraceName
, Messages
.OffsetDialog_ReferenceTime
, Messages
.OffsetDialog_OffsetTime
});
338 TreeViewerColumn column
= createTreeViewerColumn(Messages
.OffsetDialog_TraceName
, SWT
.NONE
);
339 column
.setLabelProvider(new ColumnLabelProvider() {
341 public String
getText(Object element
) {
342 return ((TmfTraceElement
) element
).getElementPath();
346 column
= createTreeViewerColumn(Messages
.OffsetDialog_OffsetTime
, SWT
.RIGHT
);
347 column
.setLabelProvider(new ColumnLabelProvider() {
349 public String
getText(Object element
) {
350 Long offset
= fOffsetMap
.get(element
);
351 if (offset
== null || offset
== 0) {
352 return ""; //$NON-NLS-1$
354 return super.getText(OFFSET_FORMAT
.format(offset
.longValue()));
357 column
.setEditingSupport(new OffsetEditingSupport(fViewer
.getViewer(), textCellEditor
));
359 column
= createTreeViewerColumn("", SWT
.NONE
); //$NON-NLS-1$
360 column
.setLabelProvider(new ColumnLabelProvider() {
362 public String
getText(Object element
) {
363 return ""; //$NON-NLS-1$
366 column
.getColumn().setWidth(TREE_EDITOR_MIN_WIDTH
);
367 column
.getColumn().setResizable(false);
368 fButtonViewerColumn
= column
;
370 column
= createTreeViewerColumn(Messages
.OffsetDialog_ReferenceTime
, SWT
.RIGHT
);
371 column
.setLabelProvider(new ColumnLabelProvider() {
373 public String
getText(Object element
) {
374 return super.getText(fRefTimeMap
.get(element
));
377 column
.setEditingSupport(new RefTimeEditingSupport(fViewer
.getViewer(), textCellEditor
));
378 fRefTimeColumn
= column
.getColumn();
380 column
= createTreeViewerColumn(Messages
.OffsetDialog_TargetTime
, SWT
.RIGHT
);
381 column
.setLabelProvider(new ColumnLabelProvider() {
383 public String
getText(Object element
) {
384 return super.getText(fTargetTimeMap
.get(element
));
387 column
.setEditingSupport(new TargetTimeEditingSupport(fViewer
.getViewer(), textCellEditor
));
388 fTargetTimeColumn
= column
.getColumn();
390 List
<TmfTraceElement
> traces
= new ArrayList
<>(fOffsetMap
.keySet());
391 Collections
.sort(traces
, new Comparator
<TmfTraceElement
>() {
393 public int compare(TmfTraceElement o1
, TmfTraceElement o2
) {
394 IPath folder1
= new Path(o1
.getElementPath()).removeLastSegments(1);
395 IPath folder2
= new Path(o2
.getElementPath()).removeLastSegments(1);
396 if (folder1
.equals(folder2
)) {
397 return o1
.getName().compareToIgnoreCase(o2
.getName());
399 if (folder1
.isPrefixOf(folder2
)) {
401 } else if (folder2
.isPrefixOf(folder1
)) {
404 return folder1
.toString().compareToIgnoreCase(folder2
.toString());
408 fViewer
.getViewer().setContentProvider(new ArrayTreeContentProvider());
409 fViewer
.getViewer().setInput(traces
);
411 /* add button as tree editors to fourth column of every item */
412 for (TreeItem treeItem
: tree
.getItems()) {
413 TreeEditor treeEditor
= new TreeEditor(tree
);
414 Button applyButton
= new Button(tree
, SWT
.PUSH
);
415 applyButton
.setText("<<"); //$NON-NLS-1$
416 applyButton
.setData(treeItem
.getData());
417 applyButton
.addSelectionListener(new SelectionAdapter() {
419 public void widgetSelected(SelectionEvent e
) {
420 TmfTraceElement traceElement
= (TmfTraceElement
) e
.widget
.getData();
421 ITmfTimestamp targetTime
= fTargetTimeMap
.get(traceElement
);
422 ITmfTimestamp refTime
= fRefTimeMap
.get(traceElement
);
423 if (targetTime
!= null && refTime
!= null) {
424 long offset
= new TmfNanoTimestamp(targetTime
).getValue() -
425 new TmfNanoTimestamp(refTime
).getValue();
426 fOffsetMap
.put(traceElement
, offset
);
427 fViewer
.getViewer().update(traceElement
, null);
431 treeEditor
.grabHorizontal
= true;
432 treeEditor
.minimumWidth
= TREE_EDITOR_MIN_WIDTH
;
433 treeEditor
.setEditor(applyButton
, treeItem
, 2);
434 treeItem
.setData(EDITOR_KEY
, applyButton
);
437 /* put temporary values in maps to pack according to time formats */
438 fRefTimeMap
.put(traces
.get(0), new TmfNanoTimestamp());
439 fTargetTimeMap
.put(traces
.get(0), new TmfNanoTimestamp());
440 fViewer
.getViewer().update(traces
.get(0), null);
441 for (final TreeColumn treeColumn
: tree
.getColumns()) {
442 if (treeColumn
.getResizable()) {
447 fTargetTimeMap
.clear();
448 fViewer
.getViewer().update(traces
.get(0), null);
450 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
451 for (ITmfTrace parentTrace
: TmfTraceManager
.getInstance().getOpenedTraces()) {
452 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(parentTrace
)) {
453 if (traceElement
.getResource().equals(trace
.getResource())) {
454 fRefTimeMap
.put(traceElement
, trace
.getStartTime());
455 fViewer
.getViewer().update(traceElement
, null);
459 if (fRefTimeMap
.get(traceElement
) != null) {
465 /* open trace when double-clicking a tree item */
466 tree
.addSelectionListener(new SelectionAdapter() {
468 public void widgetDefaultSelected(SelectionEvent e
) {
469 TmfTraceElement traceElement
= (TmfTraceElement
) e
.item
.getData();
470 TmfOpenTraceHelper
.openTraceFromElement(traceElement
);
477 private TreeViewerColumn
createTreeViewerColumn(String title
, int style
) {
478 final TreeViewerColumn viewerColumn
= new TreeViewerColumn(fViewer
.getViewer(), style
);
479 final TreeColumn column
= viewerColumn
.getColumn();
480 column
.setText(title
);
481 column
.setResizable(true);
485 private void setBasicMode() {
486 fAdvancedMode
= false;
487 fRefTimeColumn
.setData(WIDTH_KEY
, fRefTimeColumn
.getWidth());
488 fTargetTimeColumn
.setData(WIDTH_KEY
, fTargetTimeColumn
.getWidth());
489 for (TreeItem treeItem
: fViewer
.getViewer().getTree().getItems()) {
490 Control editor
= (Control
) treeItem
.getData(EDITOR_KEY
);
491 editor
.setVisible(false);
493 fTargetTimeColumn
.setWidth(0);
494 fTargetTimeColumn
.setResizable(false);
495 fRefTimeColumn
.setWidth(0);
496 fRefTimeColumn
.setResizable(false);
497 fButtonViewerColumn
.getColumn().setWidth(0);
498 fAdvancedMessageLabel
.setText(""); //$NON-NLS-1$
501 private void setAdvancedMode() {
502 fAdvancedMode
= true;
503 fButtonViewerColumn
.getColumn().setWidth(TREE_EDITOR_MIN_WIDTH
);
504 fRefTimeColumn
.setWidth((Integer
) fRefTimeColumn
.getData(WIDTH_KEY
));
505 fRefTimeColumn
.setResizable(true);
506 fTargetTimeColumn
.setWidth((Integer
) fTargetTimeColumn
.getData(WIDTH_KEY
));
507 fTargetTimeColumn
.setResizable(true);
508 for (TreeItem treeItem
: fViewer
.getViewer().getTree().getItems()) {
509 Control editor
= (Control
) treeItem
.getData(EDITOR_KEY
);
510 editor
.setVisible(true);
512 fAdvancedMessageLabel
.setText(Messages
.OffsetDialog_AdvancedMessage
);
516 * Handler for the event selected signal
519 * the event selected signal
522 public void eventSelected(final TmfEventSelectedSignal signal
) {
523 Display
.getDefault().asyncExec(new Runnable() {
526 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
527 if (traceElement
.getResource().equals(signal
.getEvent().getTrace().getResource())) {
528 fRefTimeMap
.put(traceElement
, signal
.getEvent().getTimestamp());
529 fViewer
.getViewer().update(traceElement
, null);
538 * Handler for the time selected signal
541 * the event selected signal
544 public void timeSelected(final TmfSelectionRangeUpdatedSignal signal
) {
545 Display
.getDefault().asyncExec(new Runnable() {
548 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
549 fTargetTimeMap
.put(traceElement
, signal
.getBeginTime());
550 fViewer
.getViewer().update(traceElement
, null);
557 * Handler for the trace opened signal
560 * the trace opened signal
563 public void traceOpened(final TmfTraceOpenedSignal signal
) {
564 Display
.getDefault().asyncExec(new Runnable() {
567 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(signal
.getTrace())) {
568 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
569 if (traceElement
.getResource().equals(trace
.getResource())) {
570 if (fRefTimeMap
.get(traceElement
) == null) {
571 fRefTimeMap
.put(traceElement
, trace
.getStartTime());
572 fViewer
.getViewer().update(traceElement
, null);