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