tmf: Add SWTBot tests for manually refresh of traces
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / widgets / TimeGraphControl.java
CommitLineData
837a2f8c 1/*****************************************************************************
10ad9fa6 2 * Copyright (c) 2007, 2015 Intel Corporation and others
4999a196 3 *
837a2f8c
PT
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Intel Corporation - Initial API and implementation
11 * Ruslan A. Scherbakov, Intel - Initial API and implementation
4999a196
GB
12 * Alvaro Sanchez-Leon, Ericsson - Updated for TMF
13 * Patrick Tasse, Ericsson - Refactoring
14 * Geneviève Bastien, École Polytechnique de Montréal - Move code to
15 * provide base classes for time graph view
bec1f1ac 16 * Add display of links between items
351a2391 17 * Xavier Raynaud, Kalray - Code optimization
f2ca0f69 18 * Generoso Pagano, Inria - Support for drag selection listeners
837a2f8c
PT
19 *****************************************************************************/
20
2bdf0193 21package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets;
837a2f8c
PT
22
23import java.util.ArrayList;
f1fae91f 24import java.util.Arrays;
2fe6a9ea 25import java.util.Collections;
837a2f8c 26import java.util.Iterator;
70e10acc 27import java.util.LinkedHashMap;
837a2f8c 28import java.util.List;
70e10acc 29import java.util.Map;
837a2f8c 30
0fcf3b09 31import org.eclipse.jface.action.IStatusLineManager;
837a2f8c
PT
32import org.eclipse.jface.resource.JFaceResources;
33import org.eclipse.jface.resource.LocalResourceManager;
f4617471 34import org.eclipse.jface.viewers.AbstractTreeViewer;
837a2f8c
PT
35import org.eclipse.jface.viewers.ISelection;
36import org.eclipse.jface.viewers.ISelectionChangedListener;
37import org.eclipse.jface.viewers.ISelectionProvider;
6ac5a950 38import org.eclipse.jface.viewers.ViewerFilter;
0fab12b0 39import org.eclipse.osgi.util.NLS;
837a2f8c 40import org.eclipse.swt.SWT;
837a2f8c
PT
41import org.eclipse.swt.events.FocusEvent;
42import org.eclipse.swt.events.FocusListener;
43import org.eclipse.swt.events.KeyEvent;
44import org.eclipse.swt.events.KeyListener;
27df1564
XR
45import org.eclipse.swt.events.MenuDetectEvent;
46import org.eclipse.swt.events.MenuDetectListener;
837a2f8c
PT
47import org.eclipse.swt.events.MouseEvent;
48import org.eclipse.swt.events.MouseListener;
49import org.eclipse.swt.events.MouseMoveListener;
50import org.eclipse.swt.events.MouseTrackListener;
51import org.eclipse.swt.events.MouseWheelListener;
52import org.eclipse.swt.events.PaintEvent;
837a2f8c
PT
53import org.eclipse.swt.events.SelectionListener;
54import org.eclipse.swt.events.TraverseEvent;
55import org.eclipse.swt.events.TraverseListener;
27df1564 56import org.eclipse.swt.events.TypedEvent;
837a2f8c
PT
57import org.eclipse.swt.graphics.Color;
58import org.eclipse.swt.graphics.Cursor;
59import org.eclipse.swt.graphics.GC;
60import org.eclipse.swt.graphics.Image;
61import org.eclipse.swt.graphics.Point;
62import org.eclipse.swt.graphics.Rectangle;
63import org.eclipse.swt.widgets.Composite;
64import org.eclipse.swt.widgets.Display;
65import org.eclipse.swt.widgets.Event;
66import org.eclipse.swt.widgets.Listener;
d2e4afa7
MAL
67import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
68import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
69import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
70import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
2bdf0193
AM
71import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphColorListener;
72import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider;
73import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2;
74import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener;
75import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTreeListener;
76import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem;
77import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent;
78import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent;
79import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
56b43ab2 80import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
2bdf0193
AM
81import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
82import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
83import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
84import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
837a2f8c 85
4923d7b9
PT
86import com.google.common.collect.Iterables;
87
837a2f8c
PT
88/**
89 * Time graph control implementation
90 *
837a2f8c
PT
91 * @author Alvaro Sanchez-Leon
92 * @author Patrick Tasse
93 */
496f76d3 94public class TimeGraphControl extends TimeGraphBaseControl
b698ec63
PT
95 implements FocusListener, KeyListener, MouseMoveListener, MouseListener,
96 MouseWheelListener, MouseTrackListener, TraverseListener, ISelectionProvider,
6b11be52 97 MenuDetectListener, ITmfTimeGraphDrawingHelper, ITimeGraphColorListener, Listener {
f1fae91f 98
ae09c4ad 99 /** Constant indicating that all levels of the time graph should be expanded */
f4617471
PT
100 public static final int ALL_LEVELS = AbstractTreeViewer.ALL_LEVELS;
101
837a2f8c
PT
102 private static final int DRAG_NONE = 0;
103 private static final int DRAG_TRACE_ITEM = 1;
104 private static final int DRAG_SPLIT_LINE = 2;
5b2b9bd7 105 private static final int DRAG_ZOOM = 3;
0fcf3b09 106 private static final int DRAG_SELECTION = 4;
a0a88f65 107
837a2f8c
PT
108 private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider
109
f1fae91f
PT
110 private static final double ZOOM_FACTOR = 1.5;
111 private static final double ZOOM_IN_FACTOR = 0.8;
112 private static final double ZOOM_OUT_FACTOR = 1.25;
113
0fcf3b09 114 private static final int SNAP_WIDTH = 2;
1053eaa6 115 private static final int ARROW_HOVER_MAX_DIST = 5;
0fcf3b09
PT
116
117 private static final int NO_STATUS = -1;
33fa1fc7 118 private static final int STATUS_WITHOUT_CURSOR_TIME = -2;
0fcf3b09 119
f1fae91f
PT
120 /** Resource manager */
121 private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
122
123 /** Color map for event types */
124 private Color[] fEventColorMap = null;
125
126 private ITimeDataProvider fTimeProvider;
0fcf3b09
PT
127 private IStatusLineManager fStatusLineManager = null;
128 private TimeGraphScale fTimeGraphScale = null;
129
f1fae91f 130 private boolean fIsInFocus = false;
f1fae91f
PT
131 private boolean fMouseOverSplitLine = false;
132 private int fGlobalItemHeight = CUSTOM_ITEM_HEIGHT;
b052914f 133 private boolean fBlendSubPixelEvents = false;
f1fae91f
PT
134 private int fMinimumItemWidth = 0;
135 private int fTopIndex = 0;
136 private int fDragState = DRAG_NONE;
85203d74 137 private boolean fDragBeginMarker = false;
0fcf3b09 138 private int fDragButton;
f1fae91f
PT
139 private int fDragX0 = 0;
140 private int fDragX = 0;
0fcf3b09 141 private long fDragTime0 = 0; // used to preserve accuracy of modified selection
f1fae91f
PT
142 private int fIdealNameSpace = 0;
143 private long fTime0bak;
144 private long fTime1bak;
837a2f8c 145 private ITimeGraphPresentationProvider fTimeGraphProvider = null;
f1fae91f 146 private ItemData fItemData = null;
56b43ab2
PT
147 private List<IMarkerEvent> fMarkers = null;
148 private boolean fMarkersVisible = true;
f1fae91f 149 private List<SelectionListener> fSelectionListeners;
f2ca0f69 150 private List<ITimeGraphTimeListener> fDragSelectionListeners;
507b1336
AM
151 private final List<ISelectionChangedListener> fSelectionChangedListeners = new ArrayList<>();
152 private final List<ITimeGraphTreeListener> fTreeListeners = new ArrayList<>();
153 private final List<MenuDetectListener> fTimeGraphEntryMenuListeners = new ArrayList<>();
154 private final List<MenuDetectListener> fTimeEventMenuListeners = new ArrayList<>();
0fcf3b09
PT
155 private final Cursor fDragCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_HAND);
156 private final Cursor fResizeCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_IBEAM);
157 private final Cursor fWaitCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_WAIT);
158 private final Cursor fZoomCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_SIZEWE);
507b1336 159 private final List<ViewerFilter> fFilters = new ArrayList<>();
0b5a90a0 160 private MenuDetectEvent fPendingMenuDetectEvent = null;
55d1fc96
PT
161 private boolean fGridLinesVisible = true;
162 private Color fGridLineColor = Display.getDefault().getSystemColor(SWT.COLOR_GRAY);
79ec0b89 163 private boolean fHideArrows = false;
f4617471 164 private int fAutoExpandLevel = ALL_LEVELS;
837a2f8c 165
f1fae91f
PT
166 private int fBorderWidth = 0;
167 private int fHeaderHeight = 0;
837a2f8c 168
837a2f8c
PT
169 /**
170 * Standard constructor
171 *
172 * @param parent
173 * The parent composite object
174 * @param colors
175 * The color scheme to use
176 */
177 public TimeGraphControl(Composite parent, TimeGraphColorScheme colors) {
178
b698ec63 179 super(parent, colors, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
837a2f8c 180
f1fae91f 181 fItemData = new ItemData();
837a2f8c
PT
182
183 addFocusListener(this);
184 addMouseListener(this);
185 addMouseMoveListener(this);
186 addMouseTrackListener(this);
187 addMouseWheelListener(this);
188 addTraverseListener(this);
189 addKeyListener(this);
27df1564 190 addMenuDetectListener(this);
6b11be52 191 addListener(SWT.MouseWheel, this);
837a2f8c
PT
192 }
193
194 @Override
195 public void dispose() {
196 super.dispose();
837a2f8c
PT
197 fResourceManager.dispose();
198 }
199
200 /**
201 * Sets the timegraph provider used by this timegraph viewer.
202 *
203 * @param timeGraphProvider the timegraph provider
204 */
205 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
206 fTimeGraphProvider = timeGraphProvider;
837a2f8c 207
4999a196
GB
208 if (timeGraphProvider instanceof ITimeGraphPresentationProvider2) {
209 ((ITimeGraphPresentationProvider2) timeGraphProvider).setDrawingHelper(this);
496f76d3 210 ((ITimeGraphPresentationProvider2) timeGraphProvider).addColorListener(this);
4999a196
GB
211 }
212
837a2f8c 213 StateItem[] stateItems = fTimeGraphProvider.getStateTable();
496f76d3 214 colorSettingsChanged(stateItems);
837a2f8c
PT
215 }
216
5a66cf9c
XR
217 /**
218 * Gets the timegraph provider used by this timegraph viewer.
219 *
220 * @return the timegraph provider, or <code>null</code> if not set.
5a66cf9c
XR
221 */
222 public ITimeGraphPresentationProvider getTimeGraphProvider() {
223 return fTimeGraphProvider;
224 }
225
226 /**
227 * Gets the color map used by this timegraph viewer.
228 *
229 * @return a color map, or <code>null</code> if not set.
5a66cf9c
XR
230 */
231 public Color[] getEventColorMap() {
232 return fEventColorMap;
233 }
234
837a2f8c
PT
235 /**
236 * Assign the given time provider
237 *
238 * @param timeProvider
239 * The time provider
240 */
241 public void setTimeProvider(ITimeDataProvider timeProvider) {
f1fae91f 242 fTimeProvider = timeProvider;
837a2f8c
PT
243 redraw();
244 }
245
0fcf3b09
PT
246 /**
247 * Assign the status line manager
248 *
249 * @param statusLineManager
250 * The status line manager, or null to disable status line messages
0fcf3b09
PT
251 */
252 public void setStatusLineManager(IStatusLineManager statusLineManager) {
253 if (fStatusLineManager != null && statusLineManager == null) {
254 fStatusLineManager.setMessage(""); //$NON-NLS-1$
255 }
256 fStatusLineManager = statusLineManager;
257 }
258
259 /**
260 * Assign the time graph scale
261 *
262 * @param timeGraphScale
263 * The time graph scale
0fcf3b09
PT
264 */
265 public void setTimeGraphScale(TimeGraphScale timeGraphScale) {
266 fTimeGraphScale = timeGraphScale;
267 }
268
837a2f8c
PT
269 /**
270 * Add a selection listener
271 *
272 * @param listener
273 * The listener to add
274 */
275 public void addSelectionListener(SelectionListener listener) {
276 if (listener == null) {
277 SWT.error(SWT.ERROR_NULL_ARGUMENT);
278 }
f1fae91f 279 if (null == fSelectionListeners) {
507b1336 280 fSelectionListeners = new ArrayList<>();
837a2f8c 281 }
f1fae91f 282 fSelectionListeners.add(listener);
837a2f8c
PT
283 }
284
285 /**
286 * Remove a selection listener
287 *
288 * @param listener
289 * The listener to remove
290 */
291 public void removeSelectionListener(SelectionListener listener) {
f1fae91f
PT
292 if (null != fSelectionListeners) {
293 fSelectionListeners.remove(listener);
837a2f8c
PT
294 }
295 }
296
297 /**
298 * Selection changed callback
299 */
300 public void fireSelectionChanged() {
f1fae91f
PT
301 if (null != fSelectionListeners) {
302 Iterator<SelectionListener> it = fSelectionListeners.iterator();
837a2f8c
PT
303 while (it.hasNext()) {
304 SelectionListener listener = it.next();
305 listener.widgetSelected(null);
306 }
307 }
308 }
309
310 /**
311 * Default selection callback
312 */
313 public void fireDefaultSelection() {
f1fae91f
PT
314 if (null != fSelectionListeners) {
315 Iterator<SelectionListener> it = fSelectionListeners.iterator();
837a2f8c
PT
316 while (it.hasNext()) {
317 SelectionListener listener = it.next();
318 listener.widgetDefaultSelected(null);
319 }
320 }
321 }
322
f2ca0f69
GP
323 /**
324 * Add a drag selection listener
325 *
326 * @param listener
327 * The listener to add
f2ca0f69
GP
328 */
329 public void addDragSelectionListener(ITimeGraphTimeListener listener) {
330 if (listener == null) {
331 SWT.error(SWT.ERROR_NULL_ARGUMENT);
332 }
333 if (null == fDragSelectionListeners) {
334 fDragSelectionListeners = new ArrayList<>();
335 }
336 fDragSelectionListeners.add(listener);
337 }
338
339 /**
340 * Remove a drag selection listener
341 *
342 * @param listener
343 * The listener to remove
f2ca0f69
GP
344 */
345 public void removeDragSelectionListener(ITimeGraphTimeListener listener) {
346 if (null != fDragSelectionListeners) {
347 fDragSelectionListeners.remove(listener);
348 }
349 }
350
351 /**
352 * Drag Selection changed callback
353 *
354 * @param start
355 * Time interval start
356 * @param end
357 * Time interval end
f2ca0f69
GP
358 */
359 public void fireDragSelectionChanged(long start, long end) {
360 // check for backward intervals
361 long beginTime, endTime;
362 if (start > end) {
363 beginTime = end;
364 endTime = start;
365 } else {
366 beginTime = start;
367 endTime = end;
368 }
369 // call the listeners
370 if (null != fDragSelectionListeners) {
371 Iterator<ITimeGraphTimeListener> it = fDragSelectionListeners.iterator();
372 while (it.hasNext()) {
373 ITimeGraphTimeListener listener = it.next();
374 listener.timeSelected(new TimeGraphTimeEvent(this, beginTime, endTime));
375 }
376 }
377 }
378
837a2f8c
PT
379 /**
380 * Get the traces in the model
381 *
382 * @return The array of traces
383 */
384 public ITimeGraphEntry[] getTraces() {
70e10acc 385 return fItemData.getEntries();
837a2f8c
PT
386 }
387
837a2f8c
PT
388 /**
389 * Refresh the data for the thing
390 */
391 public void refreshData() {
f1fae91f 392 fItemData.refreshData();
837a2f8c
PT
393 redraw();
394 }
395
396 /**
397 * Refresh data for the given traces
398 *
399 * @param traces
400 * The traces to refresh
401 */
402 public void refreshData(ITimeGraphEntry[] traces) {
f1fae91f 403 fItemData.refreshData(traces);
837a2f8c
PT
404 redraw();
405 }
406
bec1f1ac
GB
407 /**
408 * Refresh the links (arrows) of this widget
409 *
2fe6a9ea 410 * @param events The link event list
bec1f1ac
GB
411 */
412 public void refreshArrows(List<ILinkEvent> events) {
413 fItemData.refreshArrows(events);
414 }
415
2fe6a9ea
PT
416 /**
417 * Get the links (arrows) of this widget
418 *
419 * @return The unmodifiable link event list
420 *
34c87ae8 421 * @since 2.0
2fe6a9ea
PT
422 */
423 public List<ILinkEvent> getArrows() {
424 return Collections.unmodifiableList(fItemData.fLinks);
425 }
426
837a2f8c
PT
427 boolean ensureVisibleItem(int idx, boolean redraw) {
428 boolean changed = false;
41b5c37f
AM
429 int index = idx;
430 if (index < 0) {
f1fae91f
PT
431 for (index = 0; index < fItemData.fExpandedItems.length; index++) {
432 if (fItemData.fExpandedItems[index].fSelected) {
837a2f8c
PT
433 break;
434 }
435 }
436 }
f1fae91f 437 if (index >= fItemData.fExpandedItems.length) {
837a2f8c
PT
438 return changed;
439 }
f1fae91f 440 if (index < fTopIndex) {
41b5c37f 441 setTopIndex(index);
837a2f8c
PT
442 if (redraw) {
443 redraw();
444 }
445 changed = true;
446 } else {
447 int page = countPerPage();
f1fae91f 448 if (index >= fTopIndex + page) {
41b5c37f 449 setTopIndex(index - page + 1);
837a2f8c
PT
450 if (redraw) {
451 redraw();
452 }
453 changed = true;
454 }
455 }
456 return changed;
457 }
458
459 /**
460 * Assign the given index as the top one
461 *
462 * @param idx
463 * The index
464 */
465 public void setTopIndex(int idx) {
f1fae91f 466 int index = Math.min(idx, fItemData.fExpandedItems.length - countPerPage());
41b5c37f 467 index = Math.max(0, index);
f1fae91f 468 fTopIndex = index;
837a2f8c
PT
469 redraw();
470 }
471
f4617471 472 /**
df0e3d5f
PT
473 * Sets the auto-expand level to be used for new entries discovered when
474 * calling {@link #refreshData()} or {@link #refreshData(ITimeGraphEntry[])}
475 * . The value 0 means that there is no auto-expand; 1 means that top-level
f4617471
PT
476 * entries are expanded, but not their children; 2 means that top-level
477 * entries are expanded, and their children, but not grand-children; and so
478 * on.
479 * <p>
480 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
481 * </p>
df0e3d5f 482 *
f4617471
PT
483 * @param level
484 * non-negative level, or <code>ALL_LEVELS</code> to expand all
485 * levels of the tree
f4617471
PT
486 */
487 public void setAutoExpandLevel(int level) {
488 fAutoExpandLevel = level;
489 }
490
491 /**
492 * Returns the auto-expand level.
493 *
494 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
495 * the tree are expanded automatically
496 * @see #setAutoExpandLevel
f4617471
PT
497 */
498 public int getAutoExpandLevel() {
499 return fAutoExpandLevel;
500 }
501
df0e3d5f
PT
502 /**
503 * Get the expanded state of a given entry.
504 *
505 * @param entry
506 * The entry
507 * @return true if the entry is expanded, false if collapsed
508 * @since 2.0
509 */
510 public boolean getExpandedState(ITimeGraphEntry entry) {
511 Item item = fItemData.fItemMap.get(entry);
512 return (item != null ? item.fExpanded : false);
513 }
514
837a2f8c
PT
515 /**
516 * Set the expanded state of a given entry
517 *
518 * @param entry
519 * The entry
520 * @param expanded
521 * True if expanded, false if collapsed
522 */
523 public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
f1fae91f
PT
524 Item item = fItemData.findItem(entry);
525 if (item != null && item.fExpanded != expanded) {
526 item.fExpanded = expanded;
527 fItemData.updateExpandedItems();
837a2f8c
PT
528 redraw();
529 }
530 }
531
532 /**
533 * Collapses all nodes of the viewer's tree, starting with the root.
837a2f8c
PT
534 */
535 public void collapseAll() {
f1fae91f
PT
536 for (Item item : fItemData.fItems) {
537 item.fExpanded = false;
837a2f8c 538 }
f1fae91f 539 fItemData.updateExpandedItems();
837a2f8c
PT
540 redraw();
541 }
542
543 /**
544 * Expands all nodes of the viewer's tree, starting with the root.
837a2f8c
PT
545 */
546 public void expandAll() {
f1fae91f
PT
547 for (Item item : fItemData.fItems) {
548 item.fExpanded = true;
837a2f8c 549 }
f1fae91f 550 fItemData.updateExpandedItems();
837a2f8c
PT
551 redraw();
552 }
553
554 /**
555 * Add a tree listener
556 *
557 * @param listener
558 * The listener to add
559 */
560 public void addTreeListener(ITimeGraphTreeListener listener) {
f1fae91f
PT
561 if (!fTreeListeners.contains(listener)) {
562 fTreeListeners.add(listener);
837a2f8c
PT
563 }
564 }
565
566 /**
567 * Remove a tree listener
568 *
569 * @param listener
570 * The listener to remove
571 */
572 public void removeTreeListener(ITimeGraphTreeListener listener) {
f1fae91f
PT
573 if (fTreeListeners.contains(listener)) {
574 fTreeListeners.remove(listener);
837a2f8c
PT
575 }
576 }
577
578 /**
579 * Tree event callback
580 *
581 * @param entry
582 * The affected entry
583 * @param expanded
584 * The expanded state (true for expanded, false for collapsed)
585 */
586 public void fireTreeEvent(ITimeGraphEntry entry, boolean expanded) {
587 TimeGraphTreeExpansionEvent event = new TimeGraphTreeExpansionEvent(this, entry);
f1fae91f 588 for (ITimeGraphTreeListener listener : fTreeListeners) {
837a2f8c
PT
589 if (expanded) {
590 listener.treeExpanded(event);
591 } else {
592 listener.treeCollapsed(event);
593 }
594 }
595 }
596
27df1564
XR
597 /**
598 * Add a menu listener on {@link ITimeGraphEntry}s
ae09c4ad 599 *
27df1564
XR
600 * @param listener
601 * The listener to add
27df1564
XR
602 */
603 public void addTimeGraphEntryMenuListener(MenuDetectListener listener) {
f1fae91f
PT
604 if (!fTimeGraphEntryMenuListeners.contains(listener)) {
605 fTimeGraphEntryMenuListeners.add(listener);
27df1564
XR
606 }
607 }
608
609 /**
610 * Remove a menu listener on {@link ITimeGraphEntry}s
611 *
612 * @param listener
613 * The listener to remove
27df1564
XR
614 */
615 public void removeTimeGraphEntryMenuListener(MenuDetectListener listener) {
f1fae91f
PT
616 if (fTimeGraphEntryMenuListeners.contains(listener)) {
617 fTimeGraphEntryMenuListeners.remove(listener);
27df1564
XR
618 }
619 }
620
621 /**
622 * Menu event callback on {@link ITimeGraphEntry}s
623 *
624 * @param event
625 * The MenuDetectEvent, with field {@link TypedEvent#data} set to the selected {@link ITimeGraphEntry}
626 */
627 private void fireMenuEventOnTimeGraphEntry(MenuDetectEvent event) {
f1fae91f 628 for (MenuDetectListener listener : fTimeGraphEntryMenuListeners) {
27df1564
XR
629 listener.menuDetected(event);
630 }
631 }
632
633 /**
634 * Add a menu listener on {@link ITimeEvent}s
635 *
636 * @param listener
637 * The listener to add
27df1564
XR
638 */
639 public void addTimeEventMenuListener(MenuDetectListener listener) {
f1fae91f
PT
640 if (!fTimeEventMenuListeners.contains(listener)) {
641 fTimeEventMenuListeners.add(listener);
27df1564
XR
642 }
643 }
644
645 /**
646 * Remove a menu listener on {@link ITimeEvent}s
647 *
648 * @param listener
649 * The listener to remove
27df1564
XR
650 */
651 public void removeTimeEventMenuListener(MenuDetectListener listener) {
f1fae91f
PT
652 if (fTimeEventMenuListeners.contains(listener)) {
653 fTimeEventMenuListeners.remove(listener);
27df1564
XR
654 }
655 }
656
657 /**
658 * Menu event callback on {@link ITimeEvent}s
659 *
660 * @param event
661 * The MenuDetectEvent, with field {@link TypedEvent#data} set to the selected {@link ITimeEvent}
662 */
663 private void fireMenuEventOnTimeEvent(MenuDetectEvent event) {
f1fae91f 664 for (MenuDetectListener listener : fTimeEventMenuListeners) {
27df1564
XR
665 listener.menuDetected(event);
666 }
667 }
668
837a2f8c
PT
669 @Override
670 public ISelection getSelection() {
671 TimeGraphSelection sel = new TimeGraphSelection();
672 ITimeGraphEntry trace = getSelectedTrace();
f1fae91f 673 if (null != trace && null != fTimeProvider) {
baf92cac 674 long selectedTime = fTimeProvider.getSelectionBegin();
837a2f8c
PT
675 ITimeEvent event = Utils.findEvent(trace, selectedTime, 0);
676 if (event != null) {
677 sel.add(event);
678 } else {
679 sel.add(trace);
680 }
681 }
682 return sel;
683 }
684
685 /**
686 * Get the selection object
687 *
688 * @return The selection
689 */
690 public ISelection getSelectionTrace() {
691 TimeGraphSelection sel = new TimeGraphSelection();
692 ITimeGraphEntry trace = getSelectedTrace();
693 if (null != trace) {
694 sel.add(trace);
695 }
696 return sel;
697 }
698
699 /**
700 * Enable/disable one of the traces in the model
701 *
702 * @param n
703 * 1 to enable it, -1 to disable. The method returns immediately
704 * if another value is used.
705 */
706 public void selectTrace(int n) {
707 if ((n != 1) && (n != -1)) {
708 return;
709 }
710
711 boolean changed = false;
712 int lastSelection = -1;
f1fae91f
PT
713 for (int i = 0; i < fItemData.fExpandedItems.length; i++) {
714 Item item = fItemData.fExpandedItems[i];
715 if (item.fSelected) {
837a2f8c 716 lastSelection = i;
f1fae91f
PT
717 if ((1 == n) && (i < fItemData.fExpandedItems.length - 1)) {
718 item.fSelected = false;
719 item = fItemData.fExpandedItems[i + 1];
720 item.fSelected = true;
837a2f8c
PT
721 changed = true;
722 } else if ((-1 == n) && (i > 0)) {
f1fae91f
PT
723 item.fSelected = false;
724 item = fItemData.fExpandedItems[i - 1];
725 item.fSelected = true;
837a2f8c
PT
726 changed = true;
727 }
728 break;
729 }
730 }
731
f1fae91f
PT
732 if (lastSelection < 0 && fItemData.fExpandedItems.length > 0) {
733 Item item = fItemData.fExpandedItems[0];
734 item.fSelected = true;
837a2f8c
PT
735 changed = true;
736 }
737
738 if (changed) {
739 ensureVisibleItem(-1, false);
740 redraw();
741 fireSelectionChanged();
742 }
743 }
744
745 /**
746 * Select an event
747 *
748 * @param n
749 * 1 for next event, -1 for previous event
33fa1fc7
PT
750 * @param extend
751 * true to extend selection range, false for single selection
752 * @since 1.0
837a2f8c 753 */
33fa1fc7 754 public void selectEvent(int n, boolean extend) {
f1fae91f 755 if (null == fTimeProvider) {
837a2f8c
PT
756 return;
757 }
758 ITimeGraphEntry trace = getSelectedTrace();
759 if (trace == null) {
760 return;
761 }
33fa1fc7 762 long selectedTime = fTimeProvider.getSelectionEnd();
50d36521 763 long endTime = fTimeProvider.getMaxTime();
837a2f8c 764 ITimeEvent nextEvent;
33fa1fc7 765 if (n == -1 && selectedTime > endTime) {
837a2f8c
PT
766 nextEvent = Utils.findEvent(trace, selectedTime, 0);
767 } else {
768 nextEvent = Utils.findEvent(trace, selectedTime, n);
769 }
33fa1fc7 770 if (null == nextEvent && n == -1) {
837a2f8c
PT
771 nextEvent = Utils.getFirstEvent(trace);
772 }
773 if (null != nextEvent) {
774 long nextTime = nextEvent.getTime();
775 // If last event detected e.g. going back or not moving to a next
776 // event
777 if (nextTime <= selectedTime && n == 1) {
778 // Select to the end of this last event
779 nextTime = nextEvent.getTime() + nextEvent.getDuration();
780 // but not beyond the end of the trace
781 if (nextTime > endTime) {
782 nextTime = endTime;
783 }
f1fae91f 784 } else if (n == -1 && nextEvent.getTime() + nextEvent.getDuration() < selectedTime) {
837a2f8c 785 // for previous event go to its end time unless we were already there
f1fae91f 786 nextTime = nextEvent.getTime() + nextEvent.getDuration();
837a2f8c 787 }
33fa1fc7
PT
788 if (extend) {
789 fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), nextTime);
790 } else {
791 fTimeProvider.setSelectedTimeNotify(nextTime, true);
792 }
837a2f8c 793 fireSelectionChanged();
33fa1fc7
PT
794 } else if (n == 1) {
795 if (extend) {
796 fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), endTime);
797 } else {
798 fTimeProvider.setSelectedTimeNotify(endTime, true);
799 }
837a2f8c
PT
800 fireSelectionChanged();
801 }
33fa1fc7 802 updateStatusLine(STATUS_WITHOUT_CURSOR_TIME);
837a2f8c
PT
803 }
804
805 /**
806 * Select the next event
33fa1fc7
PT
807 *
808 * @param extend
809 * true to extend selection range, false for single selection
810 * @since 1.0
837a2f8c 811 */
33fa1fc7
PT
812 public void selectNextEvent(boolean extend) {
813 selectEvent(1, extend);
837a2f8c 814 // Notify if visible time window has been adjusted
f1fae91f 815 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
837a2f8c
PT
816 }
817
818 /**
819 * Select the previous event
33fa1fc7
PT
820 *
821 * @param extend
822 * true to extend selection range, false for single selection
823 * @since 1.0
837a2f8c 824 */
33fa1fc7
PT
825 public void selectPrevEvent(boolean extend) {
826 selectEvent(-1, extend);
837a2f8c 827 // Notify if visible time window has been adjusted
f1fae91f 828 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
837a2f8c
PT
829 }
830
831 /**
832 * Select the next trace
833 */
834 public void selectNextTrace() {
835 selectTrace(1);
836 }
837
838 /**
839 * Select the previous trace
840 */
841 public void selectPrevTrace() {
842 selectTrace(-1);
843 }
844
b698ec63
PT
845 /**
846 * Scroll left or right by one half window size
847 *
848 * @param left
849 * true to scroll left, false to scroll right
b698ec63
PT
850 */
851 public void horizontalScroll(boolean left) {
852 long time0 = fTimeProvider.getTime0();
853 long time1 = fTimeProvider.getTime1();
854 long timeMin = fTimeProvider.getMinTime();
855 long timeMax = fTimeProvider.getMaxTime();
856 long range = time1 - time0;
857 if (range <= 0) {
858 return;
859 }
860 long increment = Math.max(1, range / 2);
861 if (left) {
862 time0 = Math.max(time0 - increment, timeMin);
863 time1 = time0 + range;
864 } else {
865 time1 = Math.min(time1 + increment, timeMax);
866 time0 = time1 - range;
867 }
868 fTimeProvider.setStartFinishTimeNotify(time0, time1);
869 }
870
837a2f8c
PT
871 /**
872 * Zoom based on mouse cursor location with mouse scrolling
873 *
874 * @param zoomIn true to zoom in, false to zoom out
875 */
876 public void zoom(boolean zoomIn) {
877 int globalX = getDisplay().getCursorLocation().x;
878 Point p = toControl(globalX, 0);
f1fae91f
PT
879 int nameSpace = fTimeProvider.getNameSpace();
880 int timeSpace = fTimeProvider.getTimeSpace();
837a2f8c 881 int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x));
f1fae91f
PT
882 long time0 = fTimeProvider.getTime0();
883 long time1 = fTimeProvider.getTime1();
837a2f8c
PT
884 long interval = time1 - time0;
885 if (interval == 0) {
886 interval = 1;
887 } // to allow getting out of single point interval
888 long newInterval;
889 if (zoomIn) {
f1fae91f 890 newInterval = Math.max(Math.round(interval * ZOOM_IN_FACTOR), fTimeProvider.getMinTimeInterval());
837a2f8c 891 } else {
f1fae91f 892 newInterval = (long) Math.ceil(interval * ZOOM_OUT_FACTOR);
837a2f8c
PT
893 }
894 long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval));
895 long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval);
896 long newTime1 = newTime0 + newInterval;
10ad9fa6 897 fTimeProvider.setStartFinishTimeNotify(newTime0, newTime1);
837a2f8c
PT
898 }
899
900 /**
901 * zoom in using single click
902 */
903 public void zoomIn() {
f1fae91f
PT
904 long prevTime0 = fTimeProvider.getTime0();
905 long prevTime1 = fTimeProvider.getTime1();
906 long prevRange = prevTime1 - prevTime0;
907 if (prevRange == 0) {
3e9a3685
PT
908 return;
909 }
baf92cac
AM
910 ITimeDataProvider provider = fTimeProvider;
911 long selTime = (provider.getSelectionEnd() + provider.getSelectionBegin()) / 2;
f1fae91f
PT
912 if (selTime <= prevTime0 || selTime >= prevTime1) {
913 selTime = (prevTime0 + prevTime1) / 2;
837a2f8c 914 }
f1fae91f
PT
915 long time0 = selTime - (long) ((selTime - prevTime0) / ZOOM_FACTOR);
916 long time1 = selTime + (long) ((prevTime1 - selTime) / ZOOM_FACTOR);
837a2f8c 917
f1fae91f 918 long inaccuracy = (fTimeProvider.getMaxTime() - fTimeProvider.getMinTime()) - (time1 - time0);
837a2f8c
PT
919
920 if (inaccuracy > 0 && inaccuracy < 100) {
f1fae91f 921 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getMinTime(), fTimeProvider.getMaxTime());
837a2f8c
PT
922 return;
923 }
924
f1fae91f
PT
925 long min = fTimeProvider.getMinTimeInterval();
926 if ((time1 - time0) < min) {
927 time0 = selTime - (selTime - prevTime0) * min / prevRange;
928 time1 = time0 + min;
837a2f8c
PT
929 }
930
f1fae91f 931 fTimeProvider.setStartFinishTimeNotify(time0, time1);
837a2f8c
PT
932 }
933
934 /**
935 * zoom out using single click
936 */
937 public void zoomOut() {
f1fae91f
PT
938 long prevTime0 = fTimeProvider.getTime0();
939 long prevTime1 = fTimeProvider.getTime1();
baf92cac
AM
940 ITimeDataProvider provider = fTimeProvider;
941 long selTime = (provider.getSelectionEnd() + provider.getSelectionBegin()) / 2;
f1fae91f
PT
942 if (selTime <= prevTime0 || selTime >= prevTime1) {
943 selTime = (prevTime0 + prevTime1) / 2;
837a2f8c 944 }
f1fae91f
PT
945 long time0 = (long) (selTime - (selTime - prevTime0) * ZOOM_FACTOR);
946 long time1 = (long) (selTime + (prevTime1 - selTime) * ZOOM_FACTOR);
837a2f8c 947
f1fae91f 948 long inaccuracy = (fTimeProvider.getMaxTime() - fTimeProvider.getMinTime()) - (time1 - time0);
837a2f8c 949 if (inaccuracy > 0 && inaccuracy < 100) {
f1fae91f 950 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getMinTime(), fTimeProvider.getMaxTime());
837a2f8c
PT
951 return;
952 }
953
f1fae91f 954 fTimeProvider.setStartFinishTimeNotify(time0, time1);
837a2f8c
PT
955 }
956
55d1fc96
PT
957 /**
958 * Set the grid lines visibility. The default is true.
959 *
960 * @param visible
961 * true to show the grid lines, false otherwise
962 * @since 2.0
963 */
964 public void setGridLinesVisible(boolean visible) {
965 fGridLinesVisible = visible;
966 }
967
968 /**
969 * Get the grid lines visibility.
970 *
971 * @return true if the grid lines are visible, false otherwise
972 * @since 2.0
973 */
974 public boolean getGridLinesVisible() {
975 return fGridLinesVisible;
976 }
977
978 /**
979 * Set the grid line color. The default is SWT.COLOR_GRAY.
980 *
981 * @param color
982 * the grid line color
983 * @since 2.0
984 */
985 public void setGridLineColor(Color color) {
986 fGridLineColor = color;
987 }
988
989 /**
990 * Get the grid line color.
991 *
992 * @return the grid line color
993 * @since 2.0
994 */
995 public Color getGridLineColor() {
996 return fGridLineColor;
997 }
998
56b43ab2
PT
999 /**
1000 * Set the markers list.
1001 *
1002 * @param markers
1003 * The markers list, or null
1004 * @since 2.0
1005 */
1006 public void setMarkers(List<IMarkerEvent> markers) {
1007 fMarkers = markers;
1008 fTimeGraphScale.setMarkers(markers);
1009 }
1010
1011 /**
1012 * Get the markers list.
1013 *
1014 * @return The markers list, or null
1015 * @since 2.0
1016 */
1017 public List<IMarkerEvent> getMarkers() {
1018 return fMarkers;
1019 }
1020
1021 /**
1022 * Set the markers visibility. The default is true.
1023 *
1024 * @param visible
1025 * true to show the markers, false otherwise
1026 * @since 2.0
1027 */
1028 public void setMarkersVisible(boolean visible) {
1029 fMarkersVisible = visible;
1030 fTimeGraphScale.setMarkersVisible(visible);
1031 }
1032
1033 /**
1034 * Get the markers visibility.
1035 *
1036 * @return true if the markers are visible, false otherwise
1037 * @since 2.0
1038 */
1039 public boolean getMarkersVisible() {
1040 return fMarkersVisible;
1041 }
1042
79ec0b89
PT
1043 /**
1044 * Hide arrows
1045 *
1046 * @param hideArrows true to hide arrows
79ec0b89
PT
1047 */
1048 public void hideArrows(boolean hideArrows) {
1049 fHideArrows = hideArrows;
1050 }
1051
086f21ae
PT
1052 /**
1053 * Follow the arrow forward
33fa1fc7
PT
1054 *
1055 * @param extend
1056 * true to extend selection range, false for single selection
1057 * @since 1.0
086f21ae 1058 */
33fa1fc7 1059 public void followArrowFwd(boolean extend) {
086f21ae
PT
1060 ITimeGraphEntry trace = getSelectedTrace();
1061 if (trace == null) {
1062 return;
1063 }
33fa1fc7 1064 long selectedTime = fTimeProvider.getSelectionEnd();
086f21ae
PT
1065 for (ILinkEvent link : fItemData.fLinks) {
1066 if (link.getEntry() == trace && link.getTime() == selectedTime) {
1067 selectItem(link.getDestinationEntry(), false);
1068 if (link.getDuration() != 0) {
33fa1fc7
PT
1069 if (extend) {
1070 fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime() + link.getDuration());
1071 } else {
1072 fTimeProvider.setSelectedTimeNotify(link.getTime() + link.getDuration(), true);
1073 }
086f21ae
PT
1074 // Notify if visible time window has been adjusted
1075 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
1076 }
1077 fireSelectionChanged();
33fa1fc7 1078 updateStatusLine(STATUS_WITHOUT_CURSOR_TIME);
086f21ae
PT
1079 return;
1080 }
1081 }
33fa1fc7 1082 selectNextEvent(extend);
086f21ae
PT
1083 }
1084
1085 /**
1086 * Follow the arrow backward
33fa1fc7
PT
1087 *
1088 * @param extend
1089 * true to extend selection range, false for single selection
1090 * @since 1.0
086f21ae 1091 */
33fa1fc7 1092 public void followArrowBwd(boolean extend) {
086f21ae
PT
1093 ITimeGraphEntry trace = getSelectedTrace();
1094 if (trace == null) {
1095 return;
1096 }
33fa1fc7 1097 long selectedTime = fTimeProvider.getSelectionEnd();
086f21ae
PT
1098 for (ILinkEvent link : fItemData.fLinks) {
1099 if (link.getDestinationEntry() == trace && link.getTime() + link.getDuration() == selectedTime) {
1100 selectItem(link.getEntry(), false);
1101 if (link.getDuration() != 0) {
33fa1fc7
PT
1102 if (extend) {
1103 fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime());
1104 } else {
1105 fTimeProvider.setSelectedTimeNotify(link.getTime(), true);
1106 }
086f21ae
PT
1107 // Notify if visible time window has been adjusted
1108 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
1109 }
1110 fireSelectionChanged();
33fa1fc7 1111 updateStatusLine(STATUS_WITHOUT_CURSOR_TIME);
086f21ae
PT
1112 return;
1113 }
1114 }
33fa1fc7 1115 selectPrevEvent(extend);
086f21ae
PT
1116 }
1117
837a2f8c
PT
1118 /**
1119 * Return the currently selected trace
1120 *
1121 * @return The entry matching the trace
1122 */
1123 public ITimeGraphEntry getSelectedTrace() {
1124 ITimeGraphEntry trace = null;
1125 int idx = getSelectedIndex();
1126 if (idx >= 0) {
70e10acc 1127 trace = fItemData.fExpandedItems[idx].fEntry;
837a2f8c
PT
1128 }
1129 return trace;
1130 }
1131
1132 /**
1133 * Retrieve the index of the currently selected item
1134 *
1135 * @return The index
1136 */
1137 public int getSelectedIndex() {
1138 int idx = -1;
f1fae91f
PT
1139 for (int i = 0; i < fItemData.fExpandedItems.length; i++) {
1140 Item item = fItemData.fExpandedItems[i];
1141 if (item.fSelected) {
837a2f8c
PT
1142 idx = i;
1143 break;
1144 }
1145 }
1146 return idx;
1147 }
1148
1149 boolean toggle(int idx) {
1150 boolean toggled = false;
f1fae91f
PT
1151 if (idx >= 0 && idx < fItemData.fExpandedItems.length) {
1152 Item item = fItemData.fExpandedItems[idx];
1153 if (item.fHasChildren) {
1154 item.fExpanded = !item.fExpanded;
1155 fItemData.updateExpandedItems();
837a2f8c
PT
1156 redraw();
1157 toggled = true;
70e10acc 1158 fireTreeEvent(item.fEntry, item.fExpanded);
837a2f8c
PT
1159 }
1160 }
1161 return toggled;
1162 }
1163
bedfbbb5
XR
1164 /**
1165 * Gets the index of the item at the given location.
1166 *
1167 * @param y
1168 * the y coordinate
1169 * @return the index of the item at the given location, of -1 if none.
bedfbbb5
XR
1170 */
1171 protected int getItemIndexAtY(int y) {
837a2f8c
PT
1172 if (y < 0) {
1173 return -1;
1174 }
c004295c
PT
1175 int ySum = 0;
1176 for (int idx = fTopIndex; idx < fItemData.fExpandedItems.length; idx++) {
1177 ySum += fItemData.fExpandedItems[idx].fItemHeight;
1178 if (y < ySum) {
1179 return idx;
837a2f8c 1180 }
837a2f8c
PT
1181 }
1182 return -1;
1183 }
1184
1185 boolean isOverSplitLine(int x) {
f1fae91f 1186 if (x < 0 || null == fTimeProvider) {
837a2f8c
PT
1187 return false;
1188 }
f1fae91f 1189 int nameWidth = fTimeProvider.getNameSpace();
0fcf3b09 1190 return Math.abs(x - nameWidth) < SNAP_WIDTH;
837a2f8c
PT
1191 }
1192
bedfbbb5
XR
1193 /**
1194 * Gets the {@link ITimeGraphEntry} at the given location.
1195 *
1196 * @param pt
1197 * a point in the widget
1198 * @return the {@link ITimeGraphEntry} at this point, or <code>null</code>
1199 * if none.
bedfbbb5
XR
1200 */
1201 protected ITimeGraphEntry getEntry(Point pt) {
837a2f8c 1202 int idx = getItemIndexAtY(pt.y);
70e10acc 1203 return idx >= 0 ? fItemData.fExpandedItems[idx].fEntry : null;
837a2f8c
PT
1204 }
1205
1053eaa6
PT
1206 /**
1207 * Return the arrow event closest to the given point that is no further than
1208 * a maximum distance.
1209 *
1210 * @param pt
1211 * a point in the widget
1212 * @return The closest arrow event, or null if there is none close enough.
1053eaa6
PT
1213 */
1214 protected ILinkEvent getArrow(Point pt) {
1215 if (fHideArrows) {
1216 return null;
1217 }
1218 ILinkEvent linkEvent = null;
1219 double minDistance = Double.MAX_VALUE;
1220 for (ILinkEvent event : fItemData.fLinks) {
1221 Rectangle rect = getArrowRectangle(new Rectangle(0, 0, 0, 0), event);
1222 if (rect != null) {
1223 int x1 = rect.x;
1224 int y1 = rect.y;
1225 int x2 = x1 + rect.width;
1226 int y2 = y1 + rect.height;
1227 double d = Utils.distance(pt.x, pt.y, x1, y1, x2, y2);
1228 if (minDistance > d) {
1229 minDistance = d;
1230 linkEvent = event;
1231 }
1232 }
1233 }
1234 if (minDistance <= ARROW_HOVER_MAX_DIST) {
1235 return linkEvent;
1236 }
1237 return null;
1238 }
1239
4999a196 1240 @Override
713a70ae 1241 public int getXForTime(long time) {
f1fae91f 1242 if (null == fTimeProvider) {
713a70ae
PT
1243 return -1;
1244 }
f1fae91f
PT
1245 long time0 = fTimeProvider.getTime0();
1246 long time1 = fTimeProvider.getTime1();
b698ec63 1247 int width = getSize().x;
f1fae91f 1248 int nameSpace = fTimeProvider.getNameSpace();
713a70ae
PT
1249 double pixelsPerNanoSec = (width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (width - nameSpace - RIGHT_MARGIN) / (time1 - time0);
1250 int x = getBounds().x + nameSpace + (int) ((time - time0) * pixelsPerNanoSec);
1251 return x;
1252 }
1253
4999a196 1254 @Override
41b5c37f 1255 public long getTimeAtX(int coord) {
f1fae91f 1256 if (null == fTimeProvider) {
837a2f8c
PT
1257 return -1;
1258 }
1259 long hitTime = -1;
b698ec63 1260 Point size = getSize();
f1fae91f
PT
1261 long time0 = fTimeProvider.getTime0();
1262 long time1 = fTimeProvider.getTime1();
1263 int nameWidth = fTimeProvider.getNameSpace();
41b5c37f 1264 final int x = coord - nameWidth;
837a2f8c
PT
1265 int timeWidth = size.x - nameWidth - RIGHT_MARGIN;
1266 if (x >= 0 && size.x >= nameWidth) {
1267 if (time1 - time0 > timeWidth) {
1268 // nanosecond smaller than one pixel: use the first integer nanosecond of this pixel's time range
1269 hitTime = time0 + (long) Math.ceil((time1 - time0) * ((double) x / timeWidth));
1270 } else {
1271 // nanosecond greater than one pixel: use the nanosecond that covers this pixel start position
1272 hitTime = time0 + (long) Math.floor((time1 - time0) * ((double) x / timeWidth));
1273 }
1274 }
1275 return hitTime;
1276 }
1277
1278 void selectItem(int idx, boolean addSelection) {
1279 boolean changed = false;
1280 if (addSelection) {
f1fae91f
PT
1281 if (idx >= 0 && idx < fItemData.fExpandedItems.length) {
1282 Item item = fItemData.fExpandedItems[idx];
1283 changed = !item.fSelected;
1284 item.fSelected = true;
837a2f8c
PT
1285 }
1286 } else {
f1fae91f
PT
1287 for (int i = 0; i < fItemData.fExpandedItems.length; i++) {
1288 Item item = fItemData.fExpandedItems[i];
1289 if ((i == idx && !item.fSelected) || (idx == -1 && item.fSelected)) {
837a2f8c
PT
1290 changed = true;
1291 }
f1fae91f 1292 item.fSelected = i == idx;
837a2f8c
PT
1293 }
1294 }
1295 changed |= ensureVisibleItem(idx, true);
1296 if (changed) {
1297 redraw();
1298 }
1299 }
1300
1301 /**
1302 * Callback for item selection
1303 *
1304 * @param trace
1305 * The entry matching the trace
1306 * @param addSelection
1307 * If the selection is added or removed
1308 */
1309 public void selectItem(ITimeGraphEntry trace, boolean addSelection) {
f1fae91f 1310 int idx = fItemData.findItemIndex(trace);
837a2f8c
PT
1311 selectItem(idx, addSelection);
1312 }
1313
1314 /**
1315 * Retrieve the number of entries shown per page.
1316 *
1317 * @return The count
1318 */
1319 public int countPerPage() {
b698ec63 1320 int height = getSize().y;
837a2f8c 1321 int count = 0;
c004295c
PT
1322 int ySum = 0;
1323 for (int idx = fTopIndex; idx < fItemData.fExpandedItems.length; idx++) {
1324 ySum += fItemData.fExpandedItems[idx].fItemHeight;
1325 if (ySum >= height) {
1326 return count;
837a2f8c 1327 }
c004295c 1328 count++;
837a2f8c 1329 }
c004295c
PT
1330 for (int idx = fTopIndex - 1; idx >= 0; idx--) {
1331 ySum += fItemData.fExpandedItems[idx].fItemHeight;
1332 if (ySum >= height) {
1333 return count;
1334 }
1335 count++;
837a2f8c
PT
1336 }
1337 return count;
1338 }
1339
1340 /**
1341 * Get the index of the top element
1342 *
1343 * @return The index
1344 */
1345 public int getTopIndex() {
f1fae91f 1346 return fTopIndex;
837a2f8c
PT
1347 }
1348
1349 /**
df0e3d5f 1350 * Get the number of expanded (visible) items
837a2f8c 1351 *
df0e3d5f 1352 * @return The count of expanded (visible) items
837a2f8c
PT
1353 */
1354 public int getExpandedElementCount() {
f1fae91f 1355 return fItemData.fExpandedItems.length;
837a2f8c
PT
1356 }
1357
1358 /**
df0e3d5f 1359 * Get an array of all expanded (visible) elements
837a2f8c 1360 *
df0e3d5f 1361 * @return The expanded (visible) elements
837a2f8c
PT
1362 */
1363 public ITimeGraphEntry[] getExpandedElements() {
507b1336 1364 ArrayList<ITimeGraphEntry> elements = new ArrayList<>();
f1fae91f 1365 for (Item item : fItemData.fExpandedItems) {
70e10acc 1366 elements.add(item.fEntry);
837a2f8c
PT
1367 }
1368 return elements.toArray(new ITimeGraphEntry[0]);
1369 }
1370
83b36bb4
PT
1371 Rectangle getNameRect(Rectangle bounds, int idx, int nameWidth) {
1372 Rectangle rect = getItemRect(bounds, idx);
c004295c
PT
1373 rect.width = nameWidth;
1374 return rect;
837a2f8c
PT
1375 }
1376
83b36bb4
PT
1377 Rectangle getStatesRect(Rectangle bounds, int idx, int nameWidth) {
1378 Rectangle rect = getItemRect(bounds, idx);
1379 rect.x += nameWidth;
1380 rect.width -= nameWidth;
1381 return rect;
1382 }
1383
1384 Rectangle getItemRect(Rectangle bounds, int idx) {
c004295c
PT
1385 int ySum = 0;
1386 if (idx >= fTopIndex) {
f1fae91f
PT
1387 for (int i = fTopIndex; i < idx; i++) {
1388 ySum += fItemData.fExpandedItems[i].fItemHeight;
837a2f8c 1389 }
c004295c
PT
1390 } else {
1391 for (int i = fTopIndex - 1; i >= idx; i--) {
1392 ySum -= fItemData.fExpandedItems[i].fItemHeight;
1393 }
837a2f8c 1394 }
83b36bb4 1395 int y = bounds.y + ySum;
c004295c 1396 int height = fItemData.fExpandedItems[idx].fItemHeight;
83b36bb4 1397 return new Rectangle(bounds.x, y, bounds.width, height);
837a2f8c
PT
1398 }
1399
1400 @Override
1401 void paint(Rectangle bounds, PaintEvent e) {
1402 GC gc = e.gc;
837a2f8c 1403
f1fae91f 1404 if (bounds.width < 2 || bounds.height < 2 || null == fTimeProvider) {
837a2f8c
PT
1405 return;
1406 }
1407
f1fae91f
PT
1408 fIdealNameSpace = 0;
1409 int nameSpace = fTimeProvider.getNameSpace();
837a2f8c 1410
1c67fff3 1411 // draw the background layer
83b36bb4 1412 drawBackground(bounds, nameSpace, gc);
837a2f8c 1413
55d1fc96
PT
1414 // draw the grid lines
1415 drawGridLines(bounds, gc);
1416
56b43ab2
PT
1417 // draw the background markers
1418 drawMarkers(bounds, fTimeProvider, fMarkers, false, nameSpace, gc);
1419
1c67fff3 1420 // draw the items
f1fae91f 1421 drawItems(bounds, fTimeProvider, fItemData.fExpandedItems, fTopIndex, nameSpace, gc);
1c67fff3 1422
56b43ab2
PT
1423 // draw the foreground markers
1424 drawMarkers(bounds, fTimeProvider, fMarkers, true, nameSpace, gc);
1425
1c67fff3 1426 // draw the links (arrows)
bec1f1ac 1427 drawLinks(bounds, fTimeProvider, fItemData.fLinks, nameSpace, gc);
1c67fff3 1428
bec1f1ac 1429 fTimeGraphProvider.postDrawControl(bounds, gc);
837a2f8c 1430
0fcf3b09
PT
1431 int alpha = gc.getAlpha();
1432 gc.setAlpha(100);
1433
f1fae91f
PT
1434 long time0 = fTimeProvider.getTime0();
1435 long time1 = fTimeProvider.getTime1();
baf92cac
AM
1436 long selectionBegin = fTimeProvider.getSelectionBegin();
1437 long selectionEnd = fTimeProvider.getSelectionEnd();
837a2f8c 1438 double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0);
0fcf3b09
PT
1439 int x0 = bounds.x + nameSpace + (int) ((selectionBegin - time0) * pixelsPerNanoSec);
1440 int x1 = bounds.x + nameSpace + (int) ((selectionEnd - time0) * pixelsPerNanoSec);
1441
1442 // draw selection lines
1443 if (fDragState != DRAG_SELECTION) {
f1fae91f 1444 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME));
0fcf3b09
PT
1445 if (x0 >= nameSpace && x0 < bounds.x + bounds.width) {
1446 gc.drawLine(x0, bounds.y, x0, bounds.y + bounds.height);
1447 }
1448 if (x1 != x0) {
1449 if (x1 >= nameSpace && x1 < bounds.x + bounds.width) {
1450 gc.drawLine(x1, bounds.y, x1, bounds.y + bounds.height);
1451 }
1452 }
837a2f8c
PT
1453 }
1454
0fcf3b09
PT
1455 // draw selection background
1456 if (selectionBegin != 0 && selectionEnd != 0 && fDragState != DRAG_SELECTION) {
1457 x0 = Math.max(nameSpace, Math.min(bounds.x + bounds.width, x0));
1458 x1 = Math.max(nameSpace, Math.min(bounds.x + bounds.width, x1));
1459 gc.setBackground(getColorScheme().getBkColor(false, false, true));
1460 if (x1 - x0 > 1) {
1461 gc.fillRectangle(new Rectangle(x0 + 1, bounds.y, x1 - x0 - 1, bounds.height));
1462 } else if (x0 - x1 > 1) {
1463 gc.fillRectangle(new Rectangle(x1 + 1, bounds.y, x0 - x1 - 1, bounds.height));
1464 }
1465 }
1466
1467 // draw drag selection background
1468 if (fDragState == DRAG_ZOOM || fDragState == DRAG_SELECTION) {
1469 gc.setBackground(getColorScheme().getBkColor(false, false, true));
1470 if (fDragX0 < fDragX) {
1471 gc.fillRectangle(new Rectangle(fDragX0, bounds.y, fDragX - fDragX0, bounds.height));
1472 } else if (fDragX0 > fDragX) {
1473 gc.fillRectangle(new Rectangle(fDragX, bounds.y, fDragX0 - fDragX, bounds.height));
1474 }
1475 }
1476
1477 // draw drag line
f1fae91f
PT
1478 if (DRAG_SPLIT_LINE == fDragState) {
1479 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.BLACK));
837a2f8c 1480 gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);
0fcf3b09 1481 } else if (DRAG_ZOOM == fDragState && Math.max(fDragX, fDragX0) > nameSpace) {
f1fae91f
PT
1482 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_FOREGROUND));
1483 gc.drawLine(fDragX0, bounds.y, fDragX0, bounds.y + bounds.height - 1);
0fcf3b09
PT
1484 if (fDragX != fDragX0) {
1485 gc.drawLine(fDragX, bounds.y, fDragX, bounds.y + bounds.height - 1);
1486 }
1487 } else if (DRAG_SELECTION == fDragState && Math.max(fDragX, fDragX0) > nameSpace) {
1488 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME));
1489 gc.drawLine(fDragX0, bounds.y, fDragX0, bounds.y + bounds.height - 1);
1490 if (fDragX != fDragX0) {
1491 gc.drawLine(fDragX, bounds.y, fDragX, bounds.y + bounds.height - 1);
1492 }
f1fae91f
PT
1493 } else if (DRAG_NONE == fDragState && fMouseOverSplitLine && fTimeProvider.getNameSpace() > 0) {
1494 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.RED));
837a2f8c
PT
1495 gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);
1496 }
0fcf3b09
PT
1497
1498 gc.setAlpha(alpha);
837a2f8c
PT
1499 }
1500
83b36bb4 1501 /**
1c67fff3
PT
1502 * Draw the background layer. Fills the background of the control's name
1503 * space and states space, updates the background of items if necessary,
1504 * and draws the item's name text and middle line.
83b36bb4
PT
1505 *
1506 * @param bounds
1c67fff3 1507 * The bounds of the control
83b36bb4 1508 * @param nameSpace
1c67fff3 1509 * The name space width
83b36bb4 1510 * @param gc
1c67fff3 1511 * Graphics context
83b36bb4
PT
1512 * @since 2.0
1513 */
1514 protected void drawBackground(Rectangle bounds, int nameSpace, GC gc) {
1515 // draw empty name space background
1516 gc.setBackground(getColorScheme().getBkColor(false, false, true));
1517 drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height);
1518
1519 // draw empty states space background
1520 gc.setBackground(getColorScheme().getColor(TimeGraphColorScheme.BACKGROUND));
1521 drawBackground(gc, bounds.x + nameSpace, bounds.y, bounds.width - nameSpace, bounds.height);
1522
83b36bb4
PT
1523 for (int i = fTopIndex; i < fItemData.fExpandedItems.length; i++) {
1524 Rectangle itemRect = getItemRect(bounds, i);
1525 if (itemRect.y >= bounds.y + bounds.height) {
1526 break;
1527 }
1528 Item item = fItemData.fExpandedItems[i];
1c67fff3 1529 // draw the background of selected item and items with no time events
83b36bb4
PT
1530 if (! item.fEntry.hasTimeEvents()) {
1531 gc.setBackground(getColorScheme().getBkColorGroup(item.fSelected, fIsInFocus));
1532 gc.fillRectangle(itemRect);
1533 } else if (item.fSelected) {
1534 gc.setBackground(getColorScheme().getBkColor(true, fIsInFocus, true));
1535 gc.fillRectangle(itemRect.x, itemRect.y, nameSpace, itemRect.height);
1536 gc.setBackground(getColorScheme().getBkColor(true, fIsInFocus, false));
1537 gc.fillRectangle(nameSpace, itemRect.y, itemRect.width - nameSpace, itemRect.height);
1538 }
1c67fff3
PT
1539 // draw the name and middle line
1540 if (! item.fEntry.hasTimeEvents()) {
1541 drawName(item, itemRect, gc);
1542 } else {
1543 Rectangle nameRect = new Rectangle(itemRect.x, itemRect.y, nameSpace, itemRect.height);
1544 drawName(item, nameRect, gc);
1545 Rectangle rect = new Rectangle(nameSpace, itemRect.y, itemRect.width - nameSpace, itemRect.height);
1546 drawMidLine(rect, gc);
1547 }
83b36bb4
PT
1548 }
1549 }
1550
55d1fc96
PT
1551 /**
1552 * Draw the grid lines
1553 *
1554 * @param bounds
1c67fff3 1555 * The bounds of the control
55d1fc96 1556 * @param gc
1c67fff3 1557 * Graphics context
55d1fc96
PT
1558 * @since 2.0
1559 */
1560 public void drawGridLines(Rectangle bounds, GC gc) {
1561 if (!fGridLinesVisible) {
1562 return;
1563 }
1564 gc.setForeground(fGridLineColor);
56b43ab2 1565 gc.setAlpha(fGridLineColor.getAlpha());
55d1fc96
PT
1566 for (int x : fTimeGraphScale.getTickList()) {
1567 gc.drawLine(x, bounds.y, x, bounds.y + bounds.height);
1568 }
56b43ab2
PT
1569 gc.setAlpha(255);
1570 }
1571
1572 /**
1573 * Draw the markers
1574 *
1575 * @param bounds
1576 * The rectangle of the area
1577 * @param timeProvider
1578 * The time provider
1579 * @param markers
1580 * The list of markers
1581 * @param foreground
1582 * true to draw the foreground markers, false otherwise
1583 * @param nameSpace
1584 * The width reserved for the names
1585 * @param gc
1586 * Reference to the SWT GC object
1587 * @since 2.0
1588 */
1589 protected void drawMarkers(Rectangle bounds, ITimeDataProvider timeProvider, List<IMarkerEvent> markers, boolean foreground, int nameSpace, GC gc) {
1590 if (!fMarkersVisible || markers == null || markers.isEmpty()) {
1591 return;
1592 }
1593 gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height));
1594 /* the list can grow concurrently but cannot shrink */
1595 for (int i = 0; i < markers.size(); i++) {
1596 IMarkerEvent marker = markers.get(i);
1597 if (marker.isForeground() == foreground) {
1598 drawMarker(marker, bounds, timeProvider, nameSpace, gc);
1599 }
1600 }
1601 gc.setClipping((Rectangle) null);
1602 }
1603
1604 /**
1605 * Draw a single marker
1606 *
1607 * @param marker
1608 * The marker event
1609 * @param bounds
1610 * The bounds of the control
1611 * @param timeProvider
1612 * The time provider
1613 * @param nameSpace
1614 * The width reserved for the name
1615 * @param gc
1616 * Reference to the SWT GC object
1617 * @since 2.0
1618 */
1619 protected void drawMarker(IMarkerEvent marker, Rectangle bounds, ITimeDataProvider timeProvider, int nameSpace, GC gc) {
1620 Rectangle rect = Utils.clone(bounds);
1621 if (marker.getEntry() != null) {
1622 int index = fItemData.findItemIndex(marker.getEntry());
1623 if (index == -1) {
1624 return;
1625 }
1626 rect = getStatesRect(bounds, index, nameSpace);
1627 if (rect.y < 0 || rect.y > bounds.height) {
1628 return;
1629 }
1630 }
1631 int x0 = getXForTime(marker.getTime());
1632 int x1 = getXForTime(marker.getTime() + marker.getDuration());
1633 if (x0 > bounds.width || x1 < nameSpace) {
1634 return;
1635 }
1636 rect.x = Math.max(nameSpace, Math.min(bounds.width, x0));
1637 rect.width = Math.max(1, Math.min(bounds.width, x1) - rect.x);
1638
1639 gc.setBackground(marker.getColor());
1640 gc.setAlpha(marker.getColor().getAlpha());
1641 gc.fillRectangle(rect);
1642 gc.setAlpha(255);
1643 String label = marker.getLabel();
1644 if (label != null && marker.getEntry() != null) {
1645 gc.setForeground(marker.getColor());
1646 Utils.drawText(gc, label, rect.x - gc.stringExtent(label).x, rect.y, true);
1647 }
55d1fc96
PT
1648 }
1649
837a2f8c
PT
1650 /**
1651 * Draw many items at once
1652 *
1653 * @param bounds
1c67fff3 1654 * The bounds of the control
837a2f8c
PT
1655 * @param timeProvider
1656 * The time provider
1657 * @param items
1658 * The array items to draw
1659 * @param topIndex
1660 * The index of the first element to draw
1661 * @param nameSpace
1c67fff3 1662 * The name space width
837a2f8c 1663 * @param gc
1c67fff3 1664 * Graphics context
837a2f8c
PT
1665 */
1666 public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider,
1667 Item[] items, int topIndex, int nameSpace, GC gc) {
1668 for (int i = topIndex; i < items.length; i++) {
1669 Item item = items[i];
1670 drawItem(item, bounds, timeProvider, i, nameSpace, gc);
1671 }
837a2f8c
PT
1672 }
1673
1674 /**
1675 * Draws the item
1676 *
1c67fff3
PT
1677 * @param item
1678 * The item to draw
1679 * @param bounds
1680 * The bounds of the control
1681 * @param timeProvider
1682 * The time provider
1683 * @param i
1684 * The expanded item index
1685 * @param nameSpace
1686 * The name space width
1687 * @param gc
1688 * Graphics context
837a2f8c
PT
1689 */
1690 protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) {
83b36bb4
PT
1691 Rectangle itemRect = getItemRect(bounds, i);
1692 if (itemRect.y >= bounds.y + bounds.height) {
1693 return;
1694 }
1695
70e10acc 1696 ITimeGraphEntry entry = item.fEntry;
837a2f8c
PT
1697 long time0 = timeProvider.getTime0();
1698 long time1 = timeProvider.getTime1();
33fa1fc7 1699 long selectedTime = fTimeProvider.getSelectionEnd();
837a2f8c 1700
83b36bb4 1701 Rectangle rect = new Rectangle(nameSpace, itemRect.y, itemRect.width - nameSpace, itemRect.height);
1c67fff3 1702 if (rect.isEmpty() || (time1 <= time0)) {
837a2f8c
PT
1703 fTimeGraphProvider.postDrawEntry(entry, rect, gc);
1704 return;
1705 }
1706
f1fae91f 1707 boolean selected = item.fSelected;
837a2f8c
PT
1708 // K pixels per second
1709 double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0);
1710
70e10acc 1711 if (item.fEntry.hasTimeEvents()) {
ab0a54d5 1712 gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height));
837a2f8c 1713 fillSpace(rect, gc, selected);
83b36bb4
PT
1714 /*
1715 * State rectangle is smaller than item bounds. Use a margin height
1716 * of 3 pixels, keep at least 3 pixels for the state, but not more
1717 * than the item height. Favor the top margin for the remainder.
1718 */
1719 int height = Math.min(rect.height, Math.max(3, rect.height - 6));
1720 int margin = (rect.height - height + 1) / 2;
1721 Rectangle stateRect = new Rectangle(rect.x, rect.y + margin, rect.width, height);
837a2f8c
PT
1722
1723 long maxDuration = (timeProvider.getTimeSpace() == 0) ? Long.MAX_VALUE : 1 * (time1 - time0) / timeProvider.getTimeSpace();
1724 Iterator<ITimeEvent> iterator = entry.getTimeEventsIterator(time0, time1, maxDuration);
1725
1726 int lastX = -1;
1727 while (iterator.hasNext()) {
1728 ITimeEvent event = iterator.next();
1729 int x = rect.x + (int) ((event.getTime() - time0) * pixelsPerNanoSec);
1730 int xEnd = rect.x + (int) ((event.getTime() + event.getDuration() - time0) * pixelsPerNanoSec);
1731 if (x >= rect.x + rect.width || xEnd < rect.x) {
1732 // event is out of bounds
1733 continue;
1734 }
1735 xEnd = Math.min(rect.x + rect.width, xEnd);
1736 stateRect.x = Math.max(rect.x, x);
1737 stateRect.width = Math.max(0, xEnd - stateRect.x + 1);
1738 if (stateRect.x == lastX) {
1739 stateRect.width -= 1;
1740 if (stateRect.width > 0) {
1741 gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
1742 gc.drawPoint(stateRect.x, stateRect.y - 2);
1743 stateRect.x += 1;
1744 }
837a2f8c
PT
1745 }
1746 boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration();
f1fae91f 1747 if (drawState(getColorScheme(), event, stateRect, gc, selected, timeSelected)) {
b052914f 1748 lastX = stateRect.x;
ad128fd8 1749 }
837a2f8c 1750 }
ab0a54d5 1751 gc.setClipping((Rectangle) null);
837a2f8c
PT
1752 }
1753 fTimeGraphProvider.postDrawEntry(entry, rect, gc);
1754 }
1755
bec1f1ac
GB
1756 /**
1757 * Draw the links
1758 *
1759 * @param bounds
1c67fff3 1760 * The bounds of the control
bec1f1ac
GB
1761 * @param timeProvider
1762 * The time provider
1763 * @param links
6ae6c5bd 1764 * The list of link events
bec1f1ac 1765 * @param nameSpace
1c67fff3 1766 * The name space width
bec1f1ac 1767 * @param gc
1c67fff3 1768 * Graphics context
bec1f1ac
GB
1769 */
1770 public void drawLinks(Rectangle bounds, ITimeDataProvider timeProvider,
1771 List<ILinkEvent> links, int nameSpace, GC gc) {
79ec0b89
PT
1772 if (fHideArrows) {
1773 return;
1774 }
ab0a54d5 1775 gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height));
6ae6c5bd
PT
1776 /* the list can grow concurrently but cannot shrink */
1777 for (int i = 0; i < links.size(); i++) {
1778 drawLink(links.get(i), bounds, timeProvider, nameSpace, gc);
bec1f1ac 1779 }
ab0a54d5 1780 gc.setClipping((Rectangle) null);
bec1f1ac
GB
1781 }
1782
1783 /**
1c67fff3 1784 * Draws a link type event
bec1f1ac
GB
1785 *
1786 * @param event
1c67fff3 1787 * The link event to draw
bec1f1ac 1788 * @param bounds
1c67fff3 1789 * The bounds of the control
bec1f1ac 1790 * @param timeProvider
1c67fff3 1791 * The time provider
bec1f1ac 1792 * @param nameSpace
1c67fff3 1793 * The name space width
bec1f1ac
GB
1794 * @param gc
1795 * Graphics context
bec1f1ac
GB
1796 */
1797 protected void drawLink(ILinkEvent event, Rectangle bounds, ITimeDataProvider timeProvider, int nameSpace, GC gc) {
1053eaa6
PT
1798 drawArrow(getColorScheme(), event, getArrowRectangle(bounds, event), gc);
1799 }
1800
1801 private Rectangle getArrowRectangle(Rectangle bounds, ILinkEvent event) {
bec1f1ac
GB
1802 int srcIndex = fItemData.findItemIndex(event.getEntry());
1803 int destIndex = fItemData.findItemIndex(event.getDestinationEntry());
1804
1805 if ((srcIndex == -1) || (destIndex == -1)) {
1053eaa6 1806 return null;
bec1f1ac
GB
1807 }
1808
1053eaa6
PT
1809 Rectangle src = getStatesRect(bounds, srcIndex, fTimeProvider.getNameSpace());
1810 Rectangle dst = getStatesRect(bounds, destIndex, fTimeProvider.getNameSpace());
bec1f1ac
GB
1811
1812 int x0 = getXForTime(event.getTime());
1813 int x1 = getXForTime(event.getTime() + event.getDuration());
03ab8eeb
PT
1814
1815 // limit the x-coordinates to prevent integer overflow in calculations
1816 // and also GC.drawLine doesn't draw properly with large coordinates
1817 final int limit = Integer.MAX_VALUE / 1024;
1818 x0 = Math.max(-limit, Math.min(x0, limit));
1819 x1 = Math.max(-limit, Math.min(x1, limit));
1820
bec1f1ac
GB
1821 int y0 = src.y + src.height / 2;
1822 int y1 = dst.y + dst.height / 2;
1053eaa6 1823 return new Rectangle(x0, y0, x1 - x0, y1 - y0);
bec1f1ac
GB
1824 }
1825
1826 /**
1c67fff3 1827 * Draw an arrow
bec1f1ac
GB
1828 *
1829 * @param colors
1830 * Color scheme
1831 * @param event
1c67fff3 1832 * Time event for which we're drawing the arrow
bec1f1ac 1833 * @param rect
1c67fff3 1834 * The arrow rectangle
bec1f1ac
GB
1835 * @param gc
1836 * Graphics context
1c67fff3 1837 * @return true if the arrow was drawn
bec1f1ac
GB
1838 */
1839 protected boolean drawArrow(TimeGraphColorScheme colors, ITimeEvent event,
1840 Rectangle rect, GC gc) {
1841
a5e66312
PT
1842 if (rect == null) {
1843 return false;
1844 }
bec1f1ac
GB
1845 int colorIdx = fTimeGraphProvider.getStateTableIndex(event);
1846 if (colorIdx < 0) {
1847 return false;
1848 }
1849 boolean visible = ((rect.height == 0) && (rect.width == 0)) ? false : true;
1850
1851 if (visible) {
1852 Color stateColor = null;
1853 if (colorIdx < fEventColorMap.length) {
1854 stateColor = fEventColorMap[colorIdx];
1855 } else {
1856 stateColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
1857 }
1858
1859 gc.setForeground(stateColor);
1860 gc.setBackground(stateColor);
1861
1862 /* Draw the arrow */
1863 gc.drawLine(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
1864 drawArrowHead(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, gc);
1865
1866 }
1867 fTimeGraphProvider.postDrawEvent(event, rect, gc);
1868 return visible;
1869 }
1870
1871 /*
1872 * @author Francis Giraldeau
1873 *
1874 * Inspiration:
1875 * http://stackoverflow.com/questions/3010803/draw-arrow-on-line-algorithm
1876 *
1877 * The algorithm was taken from this site, not the code itself
1878 */
1879 private static void drawArrowHead(int x0, int y0, int x1, int y1, GC gc)
1880 {
1881 int factor = 10;
1882 double cos = 0.9510;
1883 double sin = 0.3090;
03ab8eeb
PT
1884 long lenx = x1 - x0;
1885 long leny = y1 - y0;
bec1f1ac
GB
1886 double len = Math.sqrt(lenx * lenx + leny * leny);
1887
1888 double dx = factor * lenx / len;
1889 double dy = factor * leny / len;
1890 int end1X = (int) Math.round((x1 - (dx * cos + dy * -sin)));
1891 int end1Y = (int) Math.round((y1 - (dx * sin + dy * cos)));
1892 int end2X = (int) Math.round((x1 - (dx * cos + dy * sin)));
1893 int end2Y = (int) Math.round((y1 - (dx * -sin + dy * cos)));
1894 int[] arrow = new int[] { x1, y1, end1X, end1Y, end2X, end2Y, x1, y1 };
1895 gc.fillPolygon(arrow);
1896 }
1897
a0a88f65
AM
1898 /**
1899 * Draw the name of an item.
1900 *
1901 * @param item
1902 * Item object
1903 * @param bounds
1c67fff3 1904 * The bounds of the item's name space
a0a88f65
AM
1905 * @param gc
1906 * Graphics context
1907 */
837a2f8c 1908 protected void drawName(Item item, Rectangle bounds, GC gc) {
70e10acc 1909 boolean hasTimeEvents = item.fEntry.hasTimeEvents();
837a2f8c
PT
1910
1911 // No name to be drawn
f1fae91f 1912 if (fTimeProvider.getNameSpace() == 0) {
837a2f8c
PT
1913 return;
1914 }
1915
f1fae91f
PT
1916 int leftMargin = MARGIN + item.fLevel * EXPAND_SIZE;
1917 if (item.fHasChildren) {
1918 gc.setForeground(getColorScheme().getFgColorGroup(false, false));
1919 gc.setBackground(getColorScheme().getBkColor(false, false, false));
837a2f8c
PT
1920 Rectangle rect = Utils.clone(bounds);
1921 rect.x += leftMargin;
1922 rect.y += (bounds.height - EXPAND_SIZE) / 2;
1923 rect.width = EXPAND_SIZE;
1924 rect.height = EXPAND_SIZE;
1925 gc.fillRectangle(rect);
1926 gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1);
1927 int midy = rect.y + rect.height / 2;
1928 gc.drawLine(rect.x + 2, midy, rect.x + rect.width - 3, midy);
f1fae91f 1929 if (!item.fExpanded) {
837a2f8c
PT
1930 int midx = rect.x + rect.width / 2;
1931 gc.drawLine(midx, rect.y + 2, midx, rect.y + rect.height - 3);
1932 }
1933 }
1934 leftMargin += EXPAND_SIZE + MARGIN;
1935
70e10acc 1936 Image img = fTimeGraphProvider.getItemImage(item.fEntry);
837a2f8c
PT
1937 if (img != null) {
1938 // draw icon
1939 int imgHeight = img.getImageData().height;
1940 int imgWidth = img.getImageData().width;
1941 int x = leftMargin;
1942 int y = bounds.y + (bounds.height - imgHeight) / 2;
1943 gc.drawImage(img, x, y);
1944 leftMargin += imgWidth + MARGIN;
1945 }
f1fae91f 1946 String name = item.fName;
837a2f8c 1947 Point size = gc.stringExtent(name);
f1fae91f
PT
1948 if (fIdealNameSpace < leftMargin + size.x + MARGIN) {
1949 fIdealNameSpace = leftMargin + size.x + MARGIN;
837a2f8c
PT
1950 }
1951 if (hasTimeEvents) {
1952 // cut long string with "..."
1953 int width = bounds.width - leftMargin;
1954 int cuts = 0;
1955 while (size.x > width && name.length() > 1) {
1956 cuts++;
1957 name = name.substring(0, name.length() - 1);
1958 size = gc.stringExtent(name + "..."); //$NON-NLS-1$
1959 }
1960 if (cuts > 0) {
1961 name += "..."; //$NON-NLS-1$
1962 }
1963 }
1964 Rectangle rect = Utils.clone(bounds);
1965 rect.x += leftMargin;
1966 rect.width -= leftMargin;
1967 // draw text
1968 if (rect.width > 0) {
1969 rect.y += (bounds.height - gc.stringExtent(name).y) / 2;
f1fae91f 1970 gc.setForeground(getColorScheme().getFgColor(item.fSelected, fIsInFocus));
837a2f8c
PT
1971 int textWidth = Utils.drawText(gc, name, rect, true);
1972 leftMargin += textWidth + MARGIN;
837a2f8c
PT
1973
1974 if (hasTimeEvents) {
1975 // draw middle line
1c67fff3
PT
1976 rect.x = bounds.x + leftMargin;
1977 rect.y = bounds.y;
1978 rect.width = bounds.width - rect.x;
1979 drawMidLine(rect, gc);
837a2f8c
PT
1980 }
1981 }
1982 }
1983
a0a88f65
AM
1984 /**
1985 * Draw the state (color fill)
1986 *
1987 * @param colors
1988 * Color scheme
1989 * @param event
1990 * Time event for which we're drawing the state
1991 * @param rect
1c67fff3 1992 * The state rectangle
a0a88f65
AM
1993 * @param gc
1994 * Graphics context
1995 * @param selected
1996 * Is this time event currently selected (so it appears
1997 * highlighted)
1998 * @param timeSelected
1999 * Is the timestamp currently selected
ad128fd8 2000 * @return true if the state was drawn
a0a88f65 2001 */
ad128fd8 2002 protected boolean drawState(TimeGraphColorScheme colors, ITimeEvent event,
837a2f8c
PT
2003 Rectangle rect, GC gc, boolean selected, boolean timeSelected) {
2004
2005 int colorIdx = fTimeGraphProvider.getStateTableIndex(event);
ad128fd8
PT
2006 if (colorIdx < 0 && colorIdx != ITimeGraphPresentationProvider.TRANSPARENT) {
2007 return false;
837a2f8c
PT
2008 }
2009 boolean visible = rect.width == 0 ? false : true;
b052914f 2010 rect.width = Math.max(1, rect.width);
351a2391
XR
2011 Color black = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
2012 gc.setForeground(black);
837a2f8c 2013
b052914f
PT
2014 if (colorIdx == ITimeGraphPresentationProvider.TRANSPARENT) {
2015 if (visible) {
ad128fd8 2016 // Only draw the top and bottom borders
ad128fd8
PT
2017 gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y);
2018 gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1);
2019 if (rect.width == 1) {
ad128fd8
PT
2020 gc.drawPoint(rect.x, rect.y - 2);
2021 }
837a2f8c 2022 }
b052914f
PT
2023 fTimeGraphProvider.postDrawEvent(event, rect, gc);
2024 return false;
2025 }
2026 Color stateColor = null;
2027 if (colorIdx < fEventColorMap.length) {
2028 stateColor = fEventColorMap[colorIdx];
2029 } else {
2030 stateColor = black;
2031 }
837a2f8c 2032
b052914f
PT
2033 boolean reallySelected = timeSelected && selected;
2034 // fill all rect area
2035 gc.setBackground(stateColor);
2036 if (visible) {
837a2f8c 2037 gc.fillRectangle(rect);
b052914f
PT
2038 } else if (fBlendSubPixelEvents) {
2039 gc.setAlpha(128);
2040 gc.fillRectangle(rect);
2041 gc.setAlpha(255);
2042 }
837a2f8c 2043
b052914f
PT
2044 if (reallySelected) {
2045 gc.drawLine(rect.x, rect.y - 1, rect.x + rect.width - 1, rect.y - 1);
2046 gc.drawLine(rect.x, rect.y + rect.height, rect.x + rect.width - 1, rect.y + rect.height);
2047 }
2048 if (!visible) {
837a2f8c 2049 gc.drawPoint(rect.x, rect.y - 2);
837a2f8c
PT
2050 }
2051 fTimeGraphProvider.postDrawEvent(event, rect, gc);
ad128fd8 2052 return visible;
837a2f8c
PT
2053 }
2054
a0a88f65 2055 /**
83b36bb4 2056 * Fill an item's states rectangle
a0a88f65
AM
2057 *
2058 * @param rect
1c67fff3 2059 * The states rectangle
a0a88f65
AM
2060 * @param gc
2061 * Graphics context
2062 * @param selected
83b36bb4 2063 * true if the item is selected
a0a88f65 2064 */
837a2f8c 2065 protected void fillSpace(Rectangle rect, GC gc, boolean selected) {
1c67fff3
PT
2066 /* Nothing to draw */
2067 }
2068
2069 /**
2070 * Draw a line at the middle height of a rectangle
2071 *
2072 * @param rect
2073 * The rectangle
2074 * @param gc
2075 * Graphics context
2076 */
2077 private void drawMidLine(Rectangle rect, GC gc) {
f1fae91f 2078 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.MID_LINE));
837a2f8c
PT
2079 int midy = rect.y + rect.height / 2;
2080 gc.drawLine(rect.x, midy, rect.x + rect.width, midy);
2081 }
2082
2083 @Override
2084 public void keyTraversed(TraverseEvent e) {
2085 if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) {
2086 e.doit = true;
2087 }
2088 }
2089
2090 @Override
2091 public void keyPressed(KeyEvent e) {
2092 int idx = -1;
f1fae91f 2093 if (fItemData.fExpandedItems.length == 0) {
837a2f8c
PT
2094 return;
2095 }
2096 if (SWT.HOME == e.keyCode) {
2097 idx = 0;
2098 } else if (SWT.END == e.keyCode) {
f1fae91f 2099 idx = fItemData.fExpandedItems.length - 1;
837a2f8c
PT
2100 } else if (SWT.ARROW_DOWN == e.keyCode) {
2101 idx = getSelectedIndex();
2102 if (idx < 0) {
2103 idx = 0;
f1fae91f 2104 } else if (idx < fItemData.fExpandedItems.length - 1) {
837a2f8c
PT
2105 idx++;
2106 }
2107 } else if (SWT.ARROW_UP == e.keyCode) {
2108 idx = getSelectedIndex();
2109 if (idx < 0) {
2110 idx = 0;
2111 } else if (idx > 0) {
2112 idx--;
2113 }
33fa1fc7
PT
2114 } else if (SWT.ARROW_LEFT == e.keyCode && fDragState == DRAG_NONE) {
2115 boolean extend = (e.stateMask & SWT.SHIFT) != 0;
2116 selectPrevEvent(extend);
2117 } else if (SWT.ARROW_RIGHT == e.keyCode && fDragState == DRAG_NONE) {
2118 boolean extend = (e.stateMask & SWT.SHIFT) != 0;
2119 selectNextEvent(extend);
837a2f8c
PT
2120 } else if (SWT.PAGE_DOWN == e.keyCode) {
2121 int page = countPerPage();
2122 idx = getSelectedIndex();
2123 if (idx < 0) {
2124 idx = 0;
2125 }
2126 idx += page;
f1fae91f
PT
2127 if (idx >= fItemData.fExpandedItems.length) {
2128 idx = fItemData.fExpandedItems.length - 1;
837a2f8c
PT
2129 }
2130 } else if (SWT.PAGE_UP == e.keyCode) {
2131 int page = countPerPage();
2132 idx = getSelectedIndex();
2133 if (idx < 0) {
2134 idx = 0;
2135 }
2136 idx -= page;
2137 if (idx < 0) {
2138 idx = 0;
2139 }
2140 } else if (SWT.CR == e.keyCode) {
2141 idx = getSelectedIndex();
2142 if (idx >= 0) {
f1fae91f 2143 if (fItemData.fExpandedItems[idx].fHasChildren) {
837a2f8c
PT
2144 toggle(idx);
2145 } else {
2146 fireDefaultSelection();
2147 }
2148 }
2149 idx = -1;
2150 }
2151 if (idx >= 0) {
2152 selectItem(idx, false);
2153 fireSelectionChanged();
2154 }
0fcf3b09
PT
2155 int x = toControl(e.display.getCursorLocation()).x;
2156 updateCursor(x, e.stateMask | e.keyCode);
837a2f8c
PT
2157 }
2158
2159 @Override
2160 public void keyReleased(KeyEvent e) {
0fcf3b09
PT
2161 int x = toControl(e.display.getCursorLocation()).x;
2162 updateCursor(x, e.stateMask & ~e.keyCode);
837a2f8c
PT
2163 }
2164
2165 @Override
2166 public void focusGained(FocusEvent e) {
f1fae91f 2167 fIsInFocus = true;
837a2f8c 2168 redraw();
33fa1fc7 2169 updateStatusLine(STATUS_WITHOUT_CURSOR_TIME);
837a2f8c
PT
2170 }
2171
2172 @Override
2173 public void focusLost(FocusEvent e) {
f1fae91f 2174 fIsInFocus = false;
f1fae91f 2175 if (DRAG_NONE != fDragState) {
837a2f8c 2176 setCapture(false);
f1fae91f 2177 fDragState = DRAG_NONE;
837a2f8c
PT
2178 }
2179 redraw();
0fcf3b09 2180 updateStatusLine(NO_STATUS);
837a2f8c
PT
2181 }
2182
2183 /**
2184 * @return If the current view is focused
2185 */
2186 public boolean isInFocus() {
f1fae91f 2187 return fIsInFocus;
837a2f8c
PT
2188 }
2189
2190 /**
2191 * Provide the possibility to control the wait cursor externally e.g. data
2192 * requests in progress
2193 *
2194 * @param waitInd Should we wait indefinitely?
2195 */
2196 public void waitCursor(boolean waitInd) {
2197 // Update cursor as indicated
2198 if (waitInd) {
f1fae91f 2199 setCursor(fWaitCursor);
837a2f8c
PT
2200 } else {
2201 setCursor(null);
837a2f8c 2202 }
0fcf3b09 2203 }
837a2f8c 2204
0fcf3b09
PT
2205 private void updateCursor(int x, int stateMask) {
2206 // if Wait cursor not active, check for the need to change the cursor
2207 if (getCursor() == fWaitCursor) {
2208 return;
2209 }
2210 Cursor cursor = null;
2211 if (fDragState == DRAG_SPLIT_LINE) {
2212 } else if (fDragState == DRAG_SELECTION) {
2213 cursor = fResizeCursor;
2214 } else if (fDragState == DRAG_TRACE_ITEM) {
2215 cursor = fDragCursor;
2216 } else if (fDragState == DRAG_ZOOM) {
2217 cursor = fZoomCursor;
2218 } else if ((stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
2219 cursor = fDragCursor;
2220 } else if ((stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
2221 cursor = fResizeCursor;
baf92cac
AM
2222 } else if (!isOverSplitLine(x)) {
2223 long selectionBegin = fTimeProvider.getSelectionBegin();
2224 long selectionEnd = fTimeProvider.getSelectionEnd();
2225 int xBegin = getXForTime(selectionBegin);
2226 int xEnd = getXForTime(selectionEnd);
2227 if (Math.abs(x - xBegin) < SNAP_WIDTH || Math.abs(x - xEnd) < SNAP_WIDTH) {
2228 cursor = fResizeCursor;
0fcf3b09
PT
2229 }
2230 }
2231 if (getCursor() != cursor) {
2232 setCursor(cursor);
2233 }
837a2f8c
PT
2234 }
2235
0fcf3b09 2236 private void updateStatusLine(int x) {
0fab12b0
PT
2237 // use the time provider of the time graph scale for the status line
2238 ITimeDataProvider tdp = fTimeGraphScale.getTimeProvider();
2239 if (fStatusLineManager == null || null == tdp ||
2240 tdp.getTime0() == tdp.getTime1()) {
0fcf3b09
PT
2241 return;
2242 }
0fab12b0
PT
2243 TimeFormat tf = tdp.getTimeFormat();
2244 Resolution res = Resolution.NANOSEC;
0fcf3b09 2245 StringBuilder message = new StringBuilder();
33fa1fc7
PT
2246 if ((x >= 0 || x == STATUS_WITHOUT_CURSOR_TIME) && fDragState == DRAG_NONE) {
2247 if (x != STATUS_WITHOUT_CURSOR_TIME) {
2248 long time = getTimeAtX(x);
2249 if (time >= 0) {
2250 if (tdp instanceof ITimeDataProviderConverter) {
2251 time = ((ITimeDataProviderConverter) tdp).convertTime(time);
2252 }
2253 message.append(NLS.bind("T: {0}{1} ", //$NON-NLS-1$
0fab12b0 2254 new Object[] {
33fa1fc7
PT
2255 tf == TimeFormat.CALENDAR ? Utils.formatDate(time) + ' ' : "", //$NON-NLS-1$
2256 Utils.formatTime(time, tf, res)
0fab12b0 2257 }));
0fcf3b09
PT
2258 }
2259 }
33fa1fc7
PT
2260 long selectionBegin = tdp.getSelectionBegin();
2261 long selectionEnd = tdp.getSelectionEnd();
2262 message.append(NLS.bind("T1: {0}{1}", //$NON-NLS-1$
2263 new Object[] {
2264 tf == TimeFormat.CALENDAR ? Utils.formatDate(selectionBegin) + ' ' : "", //$NON-NLS-1$
2265 Utils.formatTime(selectionBegin, tf, res)
2266 }));
2267 if (selectionBegin != selectionEnd) {
2268 message.append(NLS.bind(" T2: {0}{1} \u0394: {2}", //$NON-NLS-1$
2269 new Object[] {
2270 tf == TimeFormat.CALENDAR ? Utils.formatDate(selectionEnd) + ' ' : "", //$NON-NLS-1$
2271 Utils.formatTime(selectionEnd, tf, res),
2272 Utils.formatDelta(selectionEnd - selectionBegin, tf, res)
2273 }));
2274 }
0fcf3b09 2275 } else if (fDragState == DRAG_SELECTION || fDragState == DRAG_ZOOM) {
85203d74
PT
2276 long time0 = fDragBeginMarker ? getTimeAtX(fDragX0) : fDragTime0;
2277 long time = fDragBeginMarker ? fDragTime0 : getTimeAtX(fDragX);
0fab12b0
PT
2278 if (tdp instanceof ITimeDataProviderConverter) {
2279 time0 = ((ITimeDataProviderConverter) tdp).convertTime(time0);
2280 time = ((ITimeDataProviderConverter) tdp).convertTime(time);
837a2f8c 2281 }
0fab12b0
PT
2282 message.append(NLS.bind("T1: {0}{1} T2: {2}{3} \u0394: {4}", //$NON-NLS-1$
2283 new Object[] {
85203d74
PT
2284 tf == TimeFormat.CALENDAR ? Utils.formatDate(time0) + ' ' : "", //$NON-NLS-1$
2285 Utils.formatTime(time0, tf, res),
2286 tf == TimeFormat.CALENDAR ? Utils.formatDate(time) + ' ' : "", //$NON-NLS-1$
2287 Utils.formatTime(time, tf, res),
2288 Utils.formatDelta(time - time0, tf, res)
0fab12b0 2289 }));
837a2f8c 2290 }
0fcf3b09 2291 fStatusLineManager.setMessage(message.toString());
837a2f8c
PT
2292 }
2293
2294 @Override
2295 public void mouseMove(MouseEvent e) {
f1fae91f 2296 if (null == fTimeProvider) {
837a2f8c
PT
2297 return;
2298 }
b698ec63 2299 Point size = getSize();
f1fae91f
PT
2300 if (DRAG_TRACE_ITEM == fDragState) {
2301 int nameWidth = fTimeProvider.getNameSpace();
0fcf3b09
PT
2302 if (e.x > nameWidth && size.x > nameWidth && fDragX != e.x) {
2303 fDragX = e.x;
f1fae91f
PT
2304 double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (fTime1bak - fTime0bak);
2305 long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((fDragX - fDragX0) / pixelsPerNanoSec));
2306 long time1 = fTime1bak - timeDelta;
2307 long maxTime = fTimeProvider.getMaxTime();
837a2f8c
PT
2308 if (time1 > maxTime) {
2309 time1 = maxTime;
2310 }
f1fae91f
PT
2311 long time0 = time1 - (fTime1bak - fTime0bak);
2312 if (time0 < fTimeProvider.getMinTime()) {
2313 time0 = fTimeProvider.getMinTime();
2314 time1 = time0 + (fTime1bak - fTime0bak);
837a2f8c 2315 }
88de10c6 2316 fTimeProvider.setStartFinishTimeNotify(time0, time1);
837a2f8c 2317 }
f1fae91f
PT
2318 } else if (DRAG_SPLIT_LINE == fDragState) {
2319 fDragX = e.x;
2320 fTimeProvider.setNameSpace(e.x);
d2e4afa7 2321 TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, getTimeViewAlignmentInfo()));
0fcf3b09 2322 } else if (DRAG_SELECTION == fDragState) {
85203d74
PT
2323 if (fDragBeginMarker) {
2324 fDragX0 = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN);
2325 } else {
2326 fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN);
2327 }
baf92cac
AM
2328 redraw();
2329 fTimeGraphScale.setDragRange(fDragX0, fDragX);
f2ca0f69 2330 fireDragSelectionChanged(getTimeAtX(fDragX0), getTimeAtX(fDragX));
f1fae91f
PT
2331 } else if (DRAG_ZOOM == fDragState) {
2332 fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN);
5b2b9bd7 2333 redraw();
0fcf3b09 2334 fTimeGraphScale.setDragRange(fDragX0, fDragX);
f1fae91f 2335 } else if (DRAG_NONE == fDragState) {
837a2f8c 2336 boolean mouseOverSplitLine = isOverSplitLine(e.x);
f1fae91f 2337 if (fMouseOverSplitLine != mouseOverSplitLine) {
837a2f8c
PT
2338 redraw();
2339 }
f1fae91f 2340 fMouseOverSplitLine = mouseOverSplitLine;
837a2f8c 2341 }
0fcf3b09
PT
2342 updateCursor(e.x, e.stateMask);
2343 updateStatusLine(e.x);
837a2f8c
PT
2344 }
2345
2346 @Override
2347 public void mouseDoubleClick(MouseEvent e) {
f1fae91f 2348 if (null == fTimeProvider) {
837a2f8c
PT
2349 return;
2350 }
5b2b9bd7 2351 if (1 == e.button && (e.stateMask & SWT.BUTTON_MASK) == 0) {
f1fae91f
PT
2352 if (isOverSplitLine(e.x) && fTimeProvider.getNameSpace() != 0) {
2353 fTimeProvider.setNameSpace(fIdealNameSpace);
837a2f8c 2354 boolean mouseOverSplitLine = isOverSplitLine(e.x);
f1fae91f 2355 if (fMouseOverSplitLine != mouseOverSplitLine) {
837a2f8c
PT
2356 redraw();
2357 }
f1fae91f 2358 fMouseOverSplitLine = mouseOverSplitLine;
d2e4afa7 2359 TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, getTimeViewAlignmentInfo()));
837a2f8c
PT
2360 return;
2361 }
2362 int idx = getItemIndexAtY(e.y);
2363 if (idx >= 0) {
2364 selectItem(idx, false);
2365 fireDefaultSelection();
2366 }
2367 }
2368 }
2369
2370 @Override
2371 public void mouseDown(MouseEvent e) {
407bfdd5
PT
2372 if (fDragState != DRAG_NONE || null == fTimeProvider ||
2373 fTimeProvider.getTime0() == fTimeProvider.getTime1() ||
b698ec63 2374 getSize().x - fTimeProvider.getNameSpace() <= 0) {
837a2f8c
PT
2375 return;
2376 }
2377 int idx;
0fcf3b09 2378 if (1 == e.button && (e.stateMask & SWT.MODIFIER_MASK) == 0) {
f1fae91f
PT
2379 int nameSpace = fTimeProvider.getNameSpace();
2380 if (nameSpace != 0 && isOverSplitLine(e.x)) {
2381 fDragState = DRAG_SPLIT_LINE;
0fcf3b09 2382 fDragButton = e.button;
f1fae91f
PT
2383 fDragX = e.x;
2384 fDragX0 = fDragX;
2385 fTime0bak = fTimeProvider.getTime0();
2386 fTime1bak = fTimeProvider.getTime1();
2387 redraw();
0fcf3b09 2388 updateCursor(e.x, e.stateMask);
f1fae91f 2389 return;
837a2f8c 2390 }
0fcf3b09
PT
2391 }
2392 if (1 == e.button && ((e.stateMask & SWT.MODIFIER_MASK) == 0 || (e.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT)) {
2393 int nameSpace = fTimeProvider.getNameSpace();
837a2f8c
PT
2394 idx = getItemIndexAtY(e.y);
2395 if (idx >= 0) {
f1fae91f
PT
2396 Item item = fItemData.fExpandedItems[idx];
2397 if (item.fHasChildren && e.x < nameSpace && e.x < MARGIN + (item.fLevel + 1) * EXPAND_SIZE) {
837a2f8c 2398 toggle(idx);
0fcf3b09 2399 return;
837a2f8c
PT
2400 }
2401 selectItem(idx, false);
2402 fireSelectionChanged();
2403 } else {
2404 selectItem(idx, false); // clear selection
837a2f8c
PT
2405 fireSelectionChanged();
2406 }
0fcf3b09
PT
2407 long hitTime = getTimeAtX(e.x);
2408 if (hitTime >= 0) {
2409 setCapture(true);
2410
2411 fDragState = DRAG_SELECTION;
85203d74 2412 fDragBeginMarker = false;
0fcf3b09
PT
2413 fDragButton = e.button;
2414 fDragX = e.x;
2415 fDragX0 = fDragX;
2416 fDragTime0 = getTimeAtX(fDragX0);
baf92cac
AM
2417 long selectionBegin = fTimeProvider.getSelectionBegin();
2418 long selectionEnd = fTimeProvider.getSelectionEnd();
2419 int xBegin = getXForTime(selectionBegin);
2420 int xEnd = getXForTime(selectionEnd);
2421 if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
2422 long time = getTimeAtX(e.x);
2423 if (Math.abs(time - selectionBegin) < Math.abs(time - selectionEnd)) {
85203d74
PT
2424 fDragBeginMarker = true;
2425 fDragX = xEnd;
2426 fDragX0 = e.x;
baf92cac 2427 fDragTime0 = selectionEnd;
0fcf3b09 2428 } else {
baf92cac
AM
2429 fDragX0 = xBegin;
2430 fDragTime0 = selectionBegin;
2431 }
2432 } else {
2433 long time = getTimeAtX(e.x);
2434 if (Math.abs(e.x - xBegin) < SNAP_WIDTH && Math.abs(time - selectionBegin) <= Math.abs(time - selectionEnd)) {
85203d74
PT
2435 fDragBeginMarker = true;
2436 fDragX = xEnd;
2437 fDragX0 = e.x;
baf92cac
AM
2438 fDragTime0 = selectionEnd;
2439 } else if (Math.abs(e.x - xEnd) < SNAP_WIDTH && Math.abs(time - selectionEnd) <= Math.abs(time - selectionBegin)) {
2440 fDragX0 = xBegin;
2441 fDragTime0 = selectionBegin;
0fcf3b09
PT
2442 }
2443 }
2444 fTime0bak = fTimeProvider.getTime0();
2445 fTime1bak = fTimeProvider.getTime1();
2446 redraw();
2447 updateCursor(e.x, e.stateMask);
2448 fTimeGraphScale.setDragRange(fDragX0, fDragX);
2449 }
2450 } else if (2 == e.button || (1 == e.button && (e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL)) {
2451 long hitTime = getTimeAtX(e.x);
2452 if (hitTime > 0) {
2453 setCapture(true);
2454 fDragState = DRAG_TRACE_ITEM;
2455 fDragButton = e.button;
2456 fDragX = e.x;
2457 fDragX0 = fDragX;
2458 fTime0bak = fTimeProvider.getTime0();
2459 fTime1bak = fTimeProvider.getTime1();
2460 updateCursor(e.x, e.stateMask);
2461 }
5b2b9bd7 2462 } else if (3 == e.button) {
5b2b9bd7 2463 setCapture(true);
b698ec63 2464 fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), getSize().x - RIGHT_MARGIN);
f1fae91f 2465 fDragX0 = fDragX;
0fab12b0 2466 fDragTime0 = getTimeAtX(fDragX0);
f1fae91f 2467 fDragState = DRAG_ZOOM;
0fcf3b09
PT
2468 fDragButton = e.button;
2469 redraw();
2470 updateCursor(e.x, e.stateMask);
2471 fTimeGraphScale.setDragRange(fDragX0, fDragX);
837a2f8c
PT
2472 }
2473 }
2474
2475 @Override
2476 public void mouseUp(MouseEvent e) {
0b5a90a0
PT
2477 if (fPendingMenuDetectEvent != null && e.button == 3) {
2478 menuDetected(fPendingMenuDetectEvent);
2479 }
f1fae91f 2480 if (DRAG_NONE != fDragState) {
837a2f8c 2481 setCapture(false);
0fcf3b09
PT
2482 if (e.button == fDragButton && DRAG_TRACE_ITEM == fDragState) {
2483 if (fDragX != fDragX0) {
2484 fTimeProvider.notifyStartFinishTime();
2485 }
2486 fDragState = DRAG_NONE;
2487 } else if (e.button == fDragButton && DRAG_SPLIT_LINE == fDragState) {
2488 fDragState = DRAG_NONE;
2489 redraw();
2490 } else if (e.button == fDragButton && DRAG_SELECTION == fDragState) {
2491 if (fDragX == fDragX0) { // click without selecting anything
837a2f8c 2492 long time = getTimeAtX(e.x);
f1fae91f 2493 fTimeProvider.setSelectedTimeNotify(time, false);
e1725a05 2494 } else {
85203d74
PT
2495 long time0 = fDragBeginMarker ? getTimeAtX(fDragX0) : fDragTime0;
2496 long time1 = fDragBeginMarker ? fDragTime0 : getTimeAtX(fDragX);
2497 fTimeProvider.setSelectionRangeNotify(time0, time1);
837a2f8c 2498 }
f1fae91f 2499 fDragState = DRAG_NONE;
837a2f8c 2500 redraw();
0fcf3b09
PT
2501 fTimeGraphScale.setDragRange(-1, -1);
2502 } else if (e.button == fDragButton && DRAG_ZOOM == fDragState) {
f1fae91f
PT
2503 int nameWidth = fTimeProvider.getNameSpace();
2504 if (Math.max(fDragX, fDragX0) > nameWidth && fDragX != fDragX0) {
2505 long time0 = getTimeAtX(fDragX0);
2506 long time1 = getTimeAtX(fDragX);
5b2b9bd7 2507 if (time0 < time1) {
f1fae91f 2508 fTimeProvider.setStartFinishTimeNotify(time0, time1);
5b2b9bd7 2509 } else {
f1fae91f 2510 fTimeProvider.setStartFinishTimeNotify(time1, time0);
5b2b9bd7
PT
2511 }
2512 } else {
2513 redraw();
2514 }
f1fae91f 2515 fDragState = DRAG_NONE;
0fcf3b09 2516 fTimeGraphScale.setDragRange(-1, -1);
837a2f8c 2517 }
837a2f8c 2518 }
0fcf3b09
PT
2519 updateCursor(e.x, e.stateMask);
2520 updateStatusLine(e.x);
837a2f8c
PT
2521 }
2522
2523 @Override
2524 public void mouseEnter(MouseEvent e) {
2525 }
2526
2527 @Override
2528 public void mouseExit(MouseEvent e) {
f1fae91f
PT
2529 if (fMouseOverSplitLine) {
2530 fMouseOverSplitLine = false;
837a2f8c
PT
2531 redraw();
2532 }
33fa1fc7 2533 updateStatusLine(STATUS_WITHOUT_CURSOR_TIME);
837a2f8c
PT
2534 }
2535
2536 @Override
2537 public void mouseHover(MouseEvent e) {
2538 }
2539
2540 @Override
2541 public void mouseScrolled(MouseEvent e) {
6b11be52 2542 if (fDragState != DRAG_NONE) {
837a2f8c
PT
2543 return;
2544 }
2545 boolean zoomScroll = false;
b698ec63 2546 boolean horizontalScroll = false;
837a2f8c
PT
2547 Point p = getParent().toControl(getDisplay().getCursorLocation());
2548 Point parentSize = getParent().getSize();
2549 if (p.x >= 0 && p.x < parentSize.x && p.y >= 0 && p.y < parentSize.y) {
2550 // over the parent control
b698ec63 2551 if (e.x > getSize().x) {
e5552166 2552 // over the vertical scroll bar
837a2f8c 2553 zoomScroll = false;
b698ec63
PT
2554 } else if (e.y < 0) {
2555 // over the time scale
837a2f8c 2556 zoomScroll = true;
b698ec63
PT
2557 } else if (e.y >= getSize().y) {
2558 // over the horizontal scroll bar
2559 if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
2560 zoomScroll = true;
2561 } else {
2562 horizontalScroll = true;
2563 }
e5552166
PT
2564 } else {
2565 if (e.x < fTimeProvider.getNameSpace()) {
2566 // over the name space
2567 zoomScroll = false;
2568 } else {
2569 // over the state area
2570 if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
2571 // over the state area, CTRL pressed
2572 zoomScroll = true;
2573 } else {
2574 // over the state area, CTRL not pressed
2575 zoomScroll = false;
2576 }
2577 }
837a2f8c
PT
2578 }
2579 }
f1fae91f 2580 if (zoomScroll && fTimeProvider.getTime0() != fTimeProvider.getTime1()) {
837a2f8c
PT
2581 if (e.count > 0) {
2582 zoom(true);
2583 } else if (e.count < 0) {
2584 zoom(false);
2585 }
b698ec63
PT
2586 } else if (horizontalScroll) {
2587 horizontalScroll(e.count > 0);
837a2f8c
PT
2588 } else {
2589 setTopIndex(getTopIndex() - e.count);
2590 }
2591 }
2592
6b11be52
PT
2593 @Override
2594 public void handleEvent(Event event) {
2595 if (event.type == SWT.MouseWheel) {
2596 // prevent horizontal scrolling when the mouse wheel is used to
2597 // scroll vertically or zoom
2598 event.doit = false;
2599 }
2600 }
2601
837a2f8c
PT
2602 @Override
2603 public int getBorderWidth() {
f1fae91f 2604 return fBorderWidth;
837a2f8c
PT
2605 }
2606
2607 /**
2608 * Set the border width
2609 *
2610 * @param borderWidth
2611 * The width
2612 */
2613 public void setBorderWidth(int borderWidth) {
f1fae91f 2614 this.fBorderWidth = borderWidth;
837a2f8c
PT
2615 }
2616
2617 /**
2618 * @return The current height of the header row
2619 */
2620 public int getHeaderHeight() {
f1fae91f 2621 return fHeaderHeight;
837a2f8c
PT
2622 }
2623
2624 /**
2625 * Set the height of the header row
2626 *
2627 * @param headerHeight
2628 * The height
2629 */
2630 public void setHeaderHeight(int headerHeight) {
f1fae91f 2631 this.fHeaderHeight = headerHeight;
837a2f8c
PT
2632 }
2633
2634 /**
c004295c 2635 * @return The default height of regular item rows
837a2f8c
PT
2636 */
2637 public int getItemHeight() {
f1fae91f 2638 return fGlobalItemHeight;
837a2f8c
PT
2639 }
2640
2641 /**
c004295c 2642 * Set the default height of regular item rows.
837a2f8c
PT
2643 *
2644 * @param rowHeight
2645 * The height
2646 */
2647 public void setItemHeight(int rowHeight) {
f1fae91f 2648 this.fGlobalItemHeight = rowHeight;
63b93cd6
PT
2649 for (Item item : fItemData.fItems) {
2650 item.fItemHeight = rowHeight;
2651 }
837a2f8c
PT
2652 }
2653
c004295c
PT
2654 /**
2655 * Set the height of a specific item. Overrides the default item height.
2656 *
2657 * @param entry
2658 * A time graph entry
2659 * @param rowHeight
2660 * The height
2661 * @return true if the height is successfully stored, false otherwise
c004295c
PT
2662 */
2663 public boolean setItemHeight(ITimeGraphEntry entry, int rowHeight) {
2664 Item item = fItemData.findItem(entry);
2665 if (item != null) {
2666 item.fItemHeight = rowHeight;
2667 return true;
2668 }
2669 return false;
2670 }
2671
837a2f8c
PT
2672 /**
2673 * Set the minimum item width
2674 *
2675 * @param width The minimum width
2676 */
2677 public void setMinimumItemWidth(int width) {
f1fae91f 2678 this.fMinimumItemWidth = width;
837a2f8c
PT
2679 }
2680
2681 /**
2682 * @return The minimum item width
2683 */
2684 public int getMinimumItemWidth() {
f1fae91f 2685 return fMinimumItemWidth;
837a2f8c
PT
2686 }
2687
b052914f
PT
2688 /**
2689 * Set whether all time events with a duration shorter than one pixel should
2690 * be blended in. If false, only the first such time event will be drawn and
2691 * the subsequent time events in the same pixel will be discarded. The
2692 * default value is false.
2693 *
2694 * @param blend
2695 * true if sub-pixel events should be blended, false otherwise.
2696 * @since 2.0
2697 */
2698 public void setBlendSubPixelEvents(boolean blend) {
2699 fBlendSubPixelEvents = blend;
2700 }
2701
837a2f8c
PT
2702 @Override
2703 public void addSelectionChangedListener(ISelectionChangedListener listener) {
f1fae91f
PT
2704 if (listener != null && !fSelectionChangedListeners.contains(listener)) {
2705 fSelectionChangedListeners.add(listener);
837a2f8c
PT
2706 }
2707 }
2708
837a2f8c
PT
2709 @Override
2710 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
2711 if (listener != null) {
f1fae91f 2712 fSelectionChangedListeners.remove(listener);
837a2f8c
PT
2713 }
2714 }
2715
837a2f8c
PT
2716 @Override
2717 public void setSelection(ISelection selection) {
2718 if (selection instanceof TimeGraphSelection) {
2719 TimeGraphSelection sel = (TimeGraphSelection) selection;
2720 Object ob = sel.getFirstElement();
2721 if (ob instanceof ITimeGraphEntry) {
2722 ITimeGraphEntry trace = (ITimeGraphEntry) ob;
2723 selectItem(trace, false);
2724 }
2725 }
2726
2727 }
2728
6ac5a950
AM
2729 /**
2730 * @param filter The filter object to be attached to the view
6ac5a950
AM
2731 */
2732 public void addFilter(ViewerFilter filter) {
f1fae91f
PT
2733 if (!fFilters.contains(filter)) {
2734 fFilters.add(filter);
6ac5a950
AM
2735 }
2736 }
2737
2738 /**
2739 * @param filter The filter object to be attached to the view
6ac5a950
AM
2740 */
2741 public void removeFilter(ViewerFilter filter) {
f1fae91f 2742 fFilters.remove(filter);
6ac5a950
AM
2743 }
2744
4923d7b9
PT
2745 /**
2746 * Returns this control's filters.
2747 *
2748 * @return an array of viewer filters
2749 * @since 2.0
2750 */
2751 public ViewerFilter[] getFilters() {
2752 return Iterables.toArray(fFilters, ViewerFilter.class);
2753 }
2754
2755 /**
2756 * Sets the filters, replacing any previous filters.
2757 *
2758 * @param filters
2759 * an array of viewer filters, or null
2760 * @since 2.0
2761 */
2762 public void setFilters(ViewerFilter[] filters) {
2763 fFilters.clear();
2764 if (filters != null) {
2765 fFilters.addAll(Arrays.asList(filters));
2766 }
2767 }
2768
496f76d3
GB
2769 @Override
2770 public void colorSettingsChanged(StateItem[] stateItems) {
2771 /* Destroy previous colors from the resource manager */
517fe01b
EB
2772 if (fEventColorMap != null) {
2773 for (Color color : fEventColorMap) {
2774 fResourceManager.destroyColor(color.getRGB());
2775 }
496f76d3
GB
2776 }
2777 if (stateItems != null) {
2778 fEventColorMap = new Color[stateItems.length];
2779 for (int i = 0; i < stateItems.length; i++) {
2780 fEventColorMap[i] = fResourceManager.createColor(stateItems[i].getStateColor());
2781 }
2782 } else {
2783 fEventColorMap = new Color[] { };
2784 }
2785 redraw();
2786 }
2787
837a2f8c 2788 private class ItemData {
df0e3d5f 2789 private Map<ITimeGraphEntry, Item> fItemMap = new LinkedHashMap<>();
f1fae91f
PT
2790 private Item[] fExpandedItems = new Item[0];
2791 private Item[] fItems = new Item[0];
70e10acc 2792 private ITimeGraphEntry fRootEntries[] = new ITimeGraphEntry[0];
507b1336 2793 private List<ILinkEvent> fLinks = new ArrayList<>();
837a2f8c
PT
2794
2795 public ItemData() {
2796 }
2797
70e10acc
PT
2798 public Item findItem(ITimeGraphEntry entry) {
2799 return fItemMap.get(entry);
837a2f8c
PT
2800 }
2801
70e10acc
PT
2802 public int findItemIndex(ITimeGraphEntry entry) {
2803 Item item = fItemMap.get(entry);
2804 if (item == null) {
837a2f8c
PT
2805 return -1;
2806 }
70e10acc 2807 return item.fExpandedIndex;
837a2f8c
PT
2808 }
2809
2810 public void refreshData() {
3e9a3685 2811 ITimeGraphEntry selection = getSelectedTrace();
df0e3d5f 2812 Map<ITimeGraphEntry, Item> itemMap = new LinkedHashMap<>();
70e10acc
PT
2813 for (int i = 0; i < fRootEntries.length; i++) {
2814 ITimeGraphEntry entry = fRootEntries[i];
df0e3d5f 2815 refreshData(itemMap, null, 0, entry);
837a2f8c 2816 }
df0e3d5f 2817 fItemMap = itemMap;
70e10acc 2818 fItems = fItemMap.values().toArray(new Item[0]);
837a2f8c 2819 updateExpandedItems();
3e9a3685 2820 if (selection != null) {
f1fae91f 2821 for (Item item : fExpandedItems) {
70e10acc 2822 if (item.fEntry == selection) {
f1fae91f 2823 item.fSelected = true;
3e9a3685
PT
2824 break;
2825 }
2826 }
2827 }
837a2f8c
PT
2828 }
2829
70e10acc 2830 private void refreshData(Map<ITimeGraphEntry, Item> itemMap, Item parent, int level, ITimeGraphEntry entry) {
837a2f8c
PT
2831 Item item = new Item(entry, entry.getName(), level);
2832 if (parent != null) {
f1fae91f 2833 parent.fChildren.add(item);
837a2f8c 2834 }
c004295c
PT
2835 if (fGlobalItemHeight == CUSTOM_ITEM_HEIGHT) {
2836 item.fItemHeight = fTimeGraphProvider.getItemHeight(entry);
2837 } else {
2838 item.fItemHeight = fGlobalItemHeight;
2839 }
70e10acc 2840 itemMap.put(entry, item);
837a2f8c 2841 if (entry.hasChildren()) {
df0e3d5f
PT
2842 Item oldItem = fItemMap.get(entry);
2843 if (oldItem != null && oldItem.fHasChildren && level == oldItem.fLevel && entry.getParent() == oldItem.fEntry.getParent()) {
2844 /* existing items keep their old expanded state */
2845 item.fExpanded = oldItem.fExpanded;
2846 } else {
2847 /* new items set the expanded state according to auto-expand level */
2848 item.fExpanded = fAutoExpandLevel == ALL_LEVELS || level < fAutoExpandLevel;
2849 }
f1fae91f 2850 item.fHasChildren = true;
837a2f8c 2851 for (ITimeGraphEntry child : entry.getChildren()) {
70e10acc 2852 refreshData(itemMap, item, level + 1, child);
837a2f8c
PT
2853 }
2854 }
2855 }
2856
2857 public void updateExpandedItems() {
70e10acc
PT
2858 for (Item item : fItems) {
2859 item.fExpandedIndex = -1;
2860 }
507b1336 2861 List<Item> expandedItemList = new ArrayList<>();
70e10acc
PT
2862 for (int i = 0; i < fRootEntries.length; i++) {
2863 ITimeGraphEntry entry = fRootEntries[i];
837a2f8c
PT
2864 Item item = findItem(entry);
2865 refreshExpanded(expandedItemList, item);
2866 }
f1fae91f 2867 fExpandedItems = expandedItemList.toArray(new Item[0]);
19ed1845 2868 fTopIndex = Math.min(fTopIndex, Math.max(0, fExpandedItems.length - 1));
837a2f8c
PT
2869 }
2870
2871 private void refreshExpanded(List<Item> expandedItemList, Item item) {
6ac5a950
AM
2872 // Check for filters
2873 boolean display = true;
f1fae91f 2874 for (ViewerFilter filter : fFilters) {
70e10acc 2875 if (!filter.select(null, item.fEntry.getParent(), item.fEntry)) {
6ac5a950
AM
2876 display = false;
2877 break;
2878 }
2879 }
2880 if (display) {
70e10acc 2881 item.fExpandedIndex = expandedItemList.size();
6ac5a950 2882 expandedItemList.add(item);
f1fae91f
PT
2883 if (item.fHasChildren && item.fExpanded) {
2884 for (Item child : item.fChildren) {
6ac5a950
AM
2885 refreshExpanded(expandedItemList, child);
2886 }
837a2f8c
PT
2887 }
2888 }
2889 }
2890
70e10acc
PT
2891 public void refreshData(ITimeGraphEntry[] entries) {
2892 if (entries == null) {
70e10acc 2893 fRootEntries = null;
f1fae91f 2894 } else {
70e10acc 2895 fRootEntries = Arrays.copyOf(entries, entries.length);
837a2f8c
PT
2896 }
2897
837a2f8c
PT
2898 refreshData();
2899 }
2900
bec1f1ac
GB
2901 public void refreshArrows(List<ILinkEvent> events) {
2902 /* If links are null, reset the list */
2903 if (events != null) {
2904 fLinks = events;
2905 } else {
507b1336 2906 fLinks = new ArrayList<>();
bec1f1ac
GB
2907 }
2908 }
2909
70e10acc
PT
2910 public ITimeGraphEntry[] getEntries() {
2911 return fRootEntries;
837a2f8c 2912 }
837a2f8c
PT
2913 }
2914
2915 private class Item {
f1fae91f 2916 private boolean fExpanded;
70e10acc 2917 private int fExpandedIndex;
f1fae91f
PT
2918 private boolean fSelected;
2919 private boolean fHasChildren;
2920 private int fItemHeight;
70e10acc
PT
2921 private final int fLevel;
2922 private final List<Item> fChildren;
2923 private final String fName;
2924 private final ITimeGraphEntry fEntry;
837a2f8c 2925
70e10acc
PT
2926 public Item(ITimeGraphEntry entry, String name, int level) {
2927 this.fEntry = entry;
f1fae91f
PT
2928 this.fName = name;
2929 this.fLevel = level;
507b1336 2930 this.fChildren = new ArrayList<>();
837a2f8c
PT
2931 }
2932
2933 @Override
2934 public String toString() {
f1fae91f 2935 return fName;
837a2f8c
PT
2936 }
2937 }
2938
27df1564
XR
2939 @Override
2940 public void menuDetected(MenuDetectEvent e) {
f1fae91f 2941 if (null == fTimeProvider) {
27df1564
XR
2942 return;
2943 }
0b5a90a0
PT
2944 if (e.detail == SWT.MENU_MOUSE) {
2945 if (fPendingMenuDetectEvent == null) {
2946 /* Feature in Linux. The MenuDetectEvent is received before mouseDown.
2947 * Store the event and trigger it later just before handling mouseUp.
2948 * This allows for the method to detect if mouse is used to drag zoom.
2949 */
2950 fPendingMenuDetectEvent = e;
2951 return;
2952 }
2953 fPendingMenuDetectEvent = null;
f1fae91f 2954 if (fDragState != DRAG_ZOOM || fDragX != fDragX0) {
0b5a90a0
PT
2955 return;
2956 }
2957 } else {
f1fae91f 2958 if (fDragState != DRAG_NONE) {
0b5a90a0
PT
2959 return;
2960 }
2961 }
27df1564
XR
2962 Point p = toControl(e.x, e.y);
2963 int idx = getItemIndexAtY(p.y);
f1fae91f
PT
2964 if (idx >= 0 && idx < fItemData.fExpandedItems.length) {
2965 Item item = fItemData.fExpandedItems[idx];
70e10acc 2966 ITimeGraphEntry entry = item.fEntry;
27df1564
XR
2967 if (entry.hasTimeEvents()) {
2968 ITimeEvent event = Utils.findEvent(entry, getTimeAtX(p.x), 2);
2969 if (event != null) {
2970 e.data = event;
2971 fireMenuEventOnTimeEvent(e);
2972 return;
2973 }
2974 }
2975 e.data = entry;
2976 fireMenuEventOnTimeGraphEntry(e);
2977 }
2978 }
2979
d2e4afa7
MAL
2980 /**
2981 * Perform the alignment operation.
2982 *
2983 * @param offset
2984 * the alignment offset
2985 *
2986 * @see ITmfTimeAligned
2987 *
2988 * @since 1.0
2989 */
2990 public void performAlign(int offset) {
2991 fTimeProvider.setNameSpace(offset);
2992 }
2993
2994 /**
2995 * Return the time alignment information
2996 *
2997 * @return the time alignment information
2998 *
2999 * @see ITmfTimeAligned
3000 *
3001 * @since 1.0
3002 */
3003 public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
3004 return new TmfTimeViewAlignmentInfo(getShell(), toDisplay(0, 0), fTimeProvider.getNameSpace());
3005 }
837a2f8c 3006}
This page took 0.3008 seconds and 5 git commands to generate.