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