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
.linuxtools
.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
.linuxtools
.tmf
.core
.signal
.TmfEventSelectedSignal
;
40 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalHandler
;
41 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalManager
;
42 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTimeSynchSignal
;
43 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
44 import org
.eclipse
.linuxtools
.tmf
.core
.timestamp
.ITmfTimestamp
;
45 import org
.eclipse
.linuxtools
.tmf
.core
.timestamp
.TmfNanoTimestamp
;
46 import org
.eclipse
.linuxtools
.tmf
.core
.timestamp
.TmfTimestampFormat
;
47 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
48 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.TmfTraceManager
;
49 import org
.eclipse
.linuxtools
.tmf
.ui
.project
.model
.TmfOpenTraceHelper
;
50 import org
.eclipse
.linuxtools
.tmf
.ui
.project
.model
.TmfTraceElement
;
51 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.ArrayTreeContentProvider
;
52 import org
.eclipse
.swt
.SWT
;
53 import org
.eclipse
.swt
.custom
.TreeEditor
;
54 import org
.eclipse
.swt
.events
.DisposeEvent
;
55 import org
.eclipse
.swt
.events
.DisposeListener
;
56 import org
.eclipse
.swt
.events
.SelectionAdapter
;
57 import org
.eclipse
.swt
.events
.SelectionEvent
;
58 import org
.eclipse
.swt
.layout
.GridData
;
59 import org
.eclipse
.swt
.layout
.GridLayout
;
60 import org
.eclipse
.swt
.layout
.RowLayout
;
61 import org
.eclipse
.swt
.widgets
.Button
;
62 import org
.eclipse
.swt
.widgets
.Composite
;
63 import org
.eclipse
.swt
.widgets
.Control
;
64 import org
.eclipse
.swt
.widgets
.Display
;
65 import org
.eclipse
.swt
.widgets
.Group
;
66 import org
.eclipse
.swt
.widgets
.Label
;
67 import org
.eclipse
.swt
.widgets
.Shell
;
68 import org
.eclipse
.swt
.widgets
.Tree
;
69 import org
.eclipse
.swt
.widgets
.TreeColumn
;
70 import org
.eclipse
.swt
.widgets
.TreeItem
;
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 TreeColumn fRefTimeColumn
;
100 private TreeViewerColumn fButtonViewerColumn
;
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
.normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();
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 if (map
.get(element
) == null) {
153 return ""; //$NON-NLS-1$
155 return TIME_FORMAT
.format(map
.get(element
).normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue());
159 private class RefTimeEditingSupport
extends TimeEditingSupport
{
160 private RefTimeEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
161 super(viewer
, textCellEditor
, fRefTimeMap
);
165 private class TargetTimeEditingSupport
extends TimeEditingSupport
{
166 private TargetTimeEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
167 super(viewer
, textCellEditor
, fTargetTimeMap
);
171 private class OffsetEditingSupport
extends ColumnEditingSupport
{
172 private OffsetEditingSupport(ColumnViewer viewer
, TextCellEditor textCellEditor
) {
173 super(viewer
, textCellEditor
);
177 protected void setValue(Object element
, Object value
) {
178 if (value
instanceof String
) {
179 String string
= (String
) value
;
180 if (string
.trim().isEmpty()) {
181 fOffsetMap
.put((TmfTraceElement
) element
, 0L);
184 Long newVal
= OFFSET_FORMAT
.parseValue(string
);
185 fOffsetMap
.put((TmfTraceElement
) element
, newVal
);
186 } catch (ParseException e
) {
187 /* Ignore and reload previous value */
190 fViewer
.getViewer().update(element
, null);
195 protected Object
getValue(Object element
) {
196 if (fOffsetMap
.get(element
) == 0) {
197 return ""; //$NON-NLS-1$
199 return OFFSET_FORMAT
.format((long) fOffsetMap
.get(element
));
209 * results to put the data into
211 public OffsetDialog(Shell parent
, Map
<TmfTraceElement
, Long
> results
) {
213 setShellStyle(getShellStyle() & ~SWT
.APPLICATION_MODAL
);
214 fOffsetMap
= results
;
215 fRefTimeMap
= new HashMap
<>();
216 fTargetTimeMap
= new HashMap
<>();
220 protected boolean isResizable() {
225 protected Control
createDialogArea(Composite parent
) {
226 getShell().setText(Messages
.OffsetDialog_Title
);
227 Composite area
= (Composite
) super.createDialogArea(parent
);
228 Composite composite
= new Composite(area
, SWT
.NONE
);
229 composite
.setLayoutData(new GridData(GridData
.FILL_BOTH
));
230 GridLayout gl
= new GridLayout();
233 composite
.setLayout(new GridLayout());
234 createBasicMessage(composite
);
235 createButtonGroup(composite
);
236 createAdvancedMessage(composite
);
237 createViewer(composite
);
239 /* set label width hint equal to tree width */
240 int widthHint
= fViewer
.getViewer().getTree().computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
).x
;
241 GridData gd
= (GridData
) fBasicMessageLabel
.getLayoutData();
242 gd
.widthHint
= widthHint
;
243 gd
= (GridData
) fAdvancedMessageLabel
.getLayoutData();
244 gd
.widthHint
= widthHint
;
245 gd
= (GridData
) composite
.getLayoutData();
246 gd
.heightHint
= composite
.computeSize(widthHint
, SWT
.DEFAULT
).y
;
249 TmfSignalManager
.register(this);
250 composite
.addDisposeListener(new DisposeListener() {
252 public void widgetDisposed(DisposeEvent e
) {
253 TmfSignalManager
.deregister(this);
259 private void createBasicMessage(final Composite parent
) {
260 fBasicMessageLabel
= new Label(parent
, SWT
.WRAP
);
261 fBasicMessageLabel
.setText(Messages
.OffsetDialog_BasicMessage
);
262 GridData gd
= new GridData(SWT
.FILL
, SWT
.CENTER
, true, false);
264 gd
.heightHint
= SWT
.DEFAULT
;
265 fBasicMessageLabel
.setLayoutData(gd
);
268 private void createButtonGroup(final Composite parent
) {
269 fButtonGroup
= new Group(parent
, SWT
.SHADOW_NONE
);
270 fButtonGroup
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, false, false));
271 fButtonGroup
.setLayout(new RowLayout(SWT
.HORIZONTAL
));
273 final Button basicButton
= new Button(fButtonGroup
, SWT
.RADIO
);
274 basicButton
.setText(Messages
.OffsetDialog_BasicButton
);
275 basicButton
.addSelectionListener(new SelectionAdapter() {
277 public void widgetSelected(SelectionEvent e
) {
278 if (!basicButton
.getSelection() || !fAdvancedMode
) {
285 basicButton
.setSelection(true);
287 final Button advancedButton
= new Button(fButtonGroup
, SWT
.RADIO
);
288 advancedButton
.setText(Messages
.OffsetDialog_AdvancedButton
);
289 advancedButton
.addSelectionListener(new SelectionAdapter() {
291 public void widgetSelected(SelectionEvent e
) {
292 if (!advancedButton
.getSelection() || fAdvancedMode
) {
301 private void createAdvancedMessage(final Composite parent
) {
302 fAdvancedMessageLabel
= new Label(parent
, SWT
.WRAP
);
303 fAdvancedMessageLabel
.setText(Messages
.OffsetDialog_AdvancedMessage
);
304 GridData gd
= new GridData(SWT
.FILL
, SWT
.CENTER
, true, false);
306 gd
.heightHint
= SWT
.DEFAULT
;
307 fAdvancedMessageLabel
.setLayoutData(gd
);
310 private void createViewer(Composite parent
) {
312 // Define the TableViewer
313 fViewer
= new FilteredTree(parent
, SWT
.MULTI
| SWT
.H_SCROLL
314 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
, new PatternFilter() {
316 protected boolean isLeafMatch(Viewer viewer
, Object element
) {
317 return wordMatches(((TmfTraceElement
) element
).getElementPath());
321 // Make lines and make header visible
322 final Tree tree
= fViewer
.getViewer().getTree();
323 tree
.setHeaderVisible(true);
324 tree
.setLinesVisible(true);
326 TreeViewerFocusCellManager focusCellManager
= new TreeViewerFocusCellManager(fViewer
.getViewer(), new FocusCellOwnerDrawHighlighter(fViewer
.getViewer()));
327 ColumnViewerEditorActivationStrategy actSupport
= new ColumnViewerEditorActivationStrategy(fViewer
.getViewer());
328 TreeViewerEditor
.create(fViewer
.getViewer(), focusCellManager
, actSupport
, ColumnViewerEditor
.TABBING_HORIZONTAL
329 | ColumnViewerEditor
.TABBING_MOVE_TO_ROW_NEIGHBOR
330 | ColumnViewerEditor
.TABBING_VERTICAL
| ColumnViewerEditor
.KEYBOARD_ACTIVATION
);
332 final TextCellEditor textCellEditor
= new TextCellEditor(fViewer
.getViewer().getTree(), SWT
.RIGHT
);
334 fViewer
.getViewer().setColumnProperties(new String
[] { Messages
.OffsetDialog_TraceName
, Messages
.OffsetDialog_ReferenceTime
, Messages
.OffsetDialog_OffsetTime
});
336 TreeViewerColumn column
= createTreeViewerColumn(Messages
.OffsetDialog_TraceName
, SWT
.NONE
);
337 column
.setLabelProvider(new ColumnLabelProvider() {
339 public String
getText(Object element
) {
340 return ((TmfTraceElement
) element
).getElementPath();
344 column
= createTreeViewerColumn(Messages
.OffsetDialog_ReferenceTime
, SWT
.RIGHT
);
345 column
.setLabelProvider(new ColumnLabelProvider() {
347 public String
getText(Object element
) {
348 return super.getText(fRefTimeMap
.get(element
));
351 column
.setEditingSupport(new RefTimeEditingSupport(fViewer
.getViewer(), textCellEditor
));
352 fRefTimeColumn
= column
.getColumn();
354 column
= createTreeViewerColumn(Messages
.OffsetDialog_OffsetTime
, SWT
.RIGHT
);
355 column
.setLabelProvider(new ColumnLabelProvider() {
357 public String
getText(Object element
) {
358 if (fOffsetMap
.get(element
) != 0) {
359 return super.getText(OFFSET_FORMAT
.format((long) fOffsetMap
.get(element
)));
361 return ""; //$NON-NLS-1$
364 column
.setEditingSupport(new OffsetEditingSupport(fViewer
.getViewer(), textCellEditor
));
366 column
= createTreeViewerColumn("", SWT
.NONE
); //$NON-NLS-1$
367 column
.setLabelProvider(new ColumnLabelProvider() {
369 public String
getText(Object element
) {
370 return ""; //$NON-NLS-1$
373 column
.getColumn().setWidth(TREE_EDITOR_MIN_WIDTH
);
374 column
.getColumn().setResizable(false);
375 fButtonViewerColumn
= column
;
377 column
= createTreeViewerColumn(Messages
.OffsetDialog_TargetTime
, SWT
.RIGHT
);
378 column
.setLabelProvider(new ColumnLabelProvider() {
380 public String
getText(Object element
) {
381 return super.getText(fTargetTimeMap
.get(element
));
384 column
.setEditingSupport(new TargetTimeEditingSupport(fViewer
.getViewer(), textCellEditor
));
385 fTargetTimeColumn
= column
.getColumn();
387 List
<TmfTraceElement
> traces
= new ArrayList
<>(fOffsetMap
.keySet());
388 Collections
.sort(traces
, new Comparator
<TmfTraceElement
>() {
390 public int compare(TmfTraceElement o1
, TmfTraceElement o2
) {
391 IPath folder1
= new Path(o1
.getElementPath()).removeLastSegments(1);
392 IPath folder2
= new Path(o2
.getElementPath()).removeLastSegments(1);
393 if (folder1
.equals(folder2
)) {
394 return o1
.getName().compareToIgnoreCase(o2
.getName());
396 if (folder1
.isPrefixOf(folder2
)) {
398 } else if (folder2
.isPrefixOf(folder1
)) {
401 return folder1
.toString().compareToIgnoreCase(folder2
.toString());
405 fViewer
.getViewer().setContentProvider(new ArrayTreeContentProvider());
406 fViewer
.getViewer().setInput(traces
);
408 /* add button as tree editors to fourth column of every item */
409 for (TreeItem treeItem
: tree
.getItems()) {
410 TreeEditor treeEditor
= new TreeEditor(tree
);
411 Button applyButton
= new Button(tree
, SWT
.PUSH
);
412 applyButton
.setText("<<"); //$NON-NLS-1$
413 applyButton
.setData(treeItem
.getData());
414 applyButton
.addSelectionListener(new SelectionAdapter() {
416 public void widgetSelected(SelectionEvent e
) {
417 TmfTraceElement traceElement
= (TmfTraceElement
) e
.widget
.getData();
418 ITmfTimestamp targetTime
= fTargetTimeMap
.get(traceElement
);
419 ITmfTimestamp refTime
= fRefTimeMap
.get(traceElement
);
420 if (targetTime
!= null && refTime
!= null) {
421 long offset
= new TmfNanoTimestamp(targetTime
).getValue() -
422 new TmfNanoTimestamp(refTime
).getValue();
423 fOffsetMap
.put(traceElement
, offset
);
424 fViewer
.getViewer().update(traceElement
, null);
428 treeEditor
.grabHorizontal
= true;
429 treeEditor
.minimumWidth
= TREE_EDITOR_MIN_WIDTH
;
430 treeEditor
.setEditor(applyButton
, treeItem
, 3);
431 treeItem
.setData(EDITOR_KEY
, applyButton
);
434 /* put temporary values in maps to pack according to time formats */
435 fRefTimeMap
.put(traces
.get(0), new TmfNanoTimestamp());
436 fTargetTimeMap
.put(traces
.get(0), new TmfNanoTimestamp());
437 fViewer
.getViewer().update(traces
.get(0), null);
438 for (final TreeColumn treeColumn
: tree
.getColumns()) {
439 if (treeColumn
.getResizable()) {
444 fTargetTimeMap
.clear();
445 fViewer
.getViewer().update(traces
.get(0), null);
447 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
448 for (ITmfTrace parentTrace
: TmfTraceManager
.getInstance().getOpenedTraces()) {
449 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(parentTrace
)) {
450 if (traceElement
.getResource().equals(trace
.getResource())) {
451 fRefTimeMap
.put(traceElement
, trace
.getStartTime());
452 fViewer
.getViewer().update(traceElement
, null);
456 if (fRefTimeMap
.get(traceElement
) != null) {
462 /* open trace when double-clicking a tree item */
463 tree
.addSelectionListener(new SelectionAdapter() {
465 public void widgetDefaultSelected(SelectionEvent e
) {
466 TmfTraceElement traceElement
= (TmfTraceElement
) e
.item
.getData();
467 TmfOpenTraceHelper
.openTraceFromElement(traceElement
);
474 private TreeViewerColumn
createTreeViewerColumn(String title
, int style
) {
475 final TreeViewerColumn viewerColumn
= new TreeViewerColumn(fViewer
.getViewer(), style
);
476 final TreeColumn column
= viewerColumn
.getColumn();
477 column
.setText(title
);
478 column
.setResizable(true);
482 private void setBasicMode() {
483 fAdvancedMode
= false;
484 fRefTimeColumn
.setData(WIDTH_KEY
, fRefTimeColumn
.getWidth());
485 fTargetTimeColumn
.setData(WIDTH_KEY
, fTargetTimeColumn
.getWidth());
486 for (TreeItem treeItem
: fViewer
.getViewer().getTree().getItems()) {
487 Control editor
= (Control
) treeItem
.getData(EDITOR_KEY
);
488 editor
.setVisible(false);
490 fRefTimeColumn
.setWidth(0);
491 fRefTimeColumn
.setResizable(false);
492 fButtonViewerColumn
.getColumn().setWidth(0);
493 fTargetTimeColumn
.setWidth(0);
494 fTargetTimeColumn
.setResizable(false);
495 fAdvancedMessageLabel
.setText(""); //$NON-NLS-1$
498 private void setAdvancedMode() {
499 fAdvancedMode
= true;
500 fRefTimeColumn
.setWidth((Integer
) fRefTimeColumn
.getData(WIDTH_KEY
));
501 fRefTimeColumn
.setResizable(true);
502 fButtonViewerColumn
.getColumn().setWidth(TREE_EDITOR_MIN_WIDTH
);
503 fTargetTimeColumn
.setWidth((Integer
) fTargetTimeColumn
.getData(WIDTH_KEY
));
504 fTargetTimeColumn
.setResizable(true);
505 for (TreeItem treeItem
: fViewer
.getViewer().getTree().getItems()) {
506 Control editor
= (Control
) treeItem
.getData(EDITOR_KEY
);
507 editor
.setVisible(true);
509 fAdvancedMessageLabel
.setText(Messages
.OffsetDialog_AdvancedMessage
);
513 * Handler for the event selected signal
516 * the event selected signal
519 public void eventSelected(final TmfEventSelectedSignal signal
) {
520 Display
.getDefault().asyncExec(new Runnable() {
523 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
524 if (traceElement
.getResource().equals(signal
.getEvent().getTrace().getResource())) {
525 fRefTimeMap
.put(traceElement
, signal
.getEvent().getTimestamp());
526 fViewer
.getViewer().update(traceElement
, null);
535 * Handler for the time selected signal
538 * the event selected signal
541 public void timeSelected(final TmfTimeSynchSignal signal
) {
542 Display
.getDefault().asyncExec(new Runnable() {
545 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
546 fTargetTimeMap
.put(traceElement
, signal
.getBeginTime());
547 fViewer
.getViewer().update(traceElement
, null);
554 * Handler for the trace opened signal
557 * the trace opened signal
560 public void traceOpened(final TmfTraceOpenedSignal signal
) {
561 Display
.getDefault().asyncExec(new Runnable() {
564 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(signal
.getTrace())) {
565 for (TmfTraceElement traceElement
: fOffsetMap
.keySet()) {
566 if (traceElement
.getResource().equals(trace
.getResource())) {
567 if (fRefTimeMap
.get(traceElement
) == null) {
568 fRefTimeMap
.put(traceElement
, trace
.getStartTime());
569 fViewer
.getViewer().update(traceElement
, null);