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