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