2 * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 package org
.lttng
.scope
.tmf2
.views
.ui
.timeline
.widgets
.timegraph
;
12 import static java
.util
.Objects
.requireNonNull
;
14 import java
.util
.Arrays
;
16 import java
.util
.Objects
;
18 import org
.eclipse
.jdt
.annotation
.Nullable
;
19 import org
.lttng
.scope
.tmf2
.views
.core
.TimeRange
;
20 import org
.lttng
.scope
.tmf2
.views
.core
.timegraph
.model
.render
.LineThickness
;
21 import org
.lttng
.scope
.tmf2
.views
.core
.timegraph
.model
.render
.states
.TimeGraphStateInterval
;
22 import org
.lttng
.scope
.tmf2
.views
.ui
.jfx
.CountingGridPane
;
23 import org
.lttng
.scope
.tmf2
.views
.ui
.jfx
.JfxColorFactory
;
24 import org
.lttng
.scope
.tmf2
.views
.ui
.jfx
.JfxUtils
;
25 import org
.lttng
.scope
.tmf2
.views
.ui
.timeline
.DebugOptions
;
27 import com
.google
.common
.base
.MoreObjects
;
29 import javafx
.application
.Platform
;
30 import javafx
.geometry
.Point2D
;
31 import javafx
.scene
.Node
;
32 import javafx
.scene
.control
.Tooltip
;
33 import javafx
.scene
.input
.MouseButton
;
34 import javafx
.scene
.paint
.Paint
;
35 import javafx
.scene
.shape
.Rectangle
;
36 import javafx
.scene
.text
.Text
;
39 * {@link Rectangle} object used to draw states in the timegraph. It attaches
40 * the {@link TimeGraphStateInterval} that represents this state.
42 * @author Alexandre Montplaisir
44 public class StateRectangle
extends Rectangle
{
46 private final TimeGraphWidget fWidget
;
47 private final int fEntryIndex
;
48 private final TimeGraphStateInterval fInterval
;
50 private @Nullable transient Paint fBaseColor
;
51 private @Nullable transient Paint fSelectedColor
;
53 private @Nullable Tooltip fTooltip
= null;
59 * The viewer in which the rectangle will be placed
61 * The source interval model object
63 * The index of the entry to which this state belongs.
65 public StateRectangle(TimeGraphWidget viewer
, TimeGraphStateInterval interval
, int entryIndex
) {
67 fEntryIndex
= entryIndex
;
71 * It is possible, especially when re-opening already-indexed traces,
72 * that the indexer and the state system do not report the same
73 * start/end times. Make sure to clamp the interval's bounds to the
76 TimeRange traceRange
= viewer
.getControl().getViewContext().getCurrentTraceFullRange();
77 long traceStart
= traceRange
.getStart();
78 long intervalStart
= interval
.getStartTime();
79 double xStart
= viewer
.timestampToPaneXPos(Math
.max(traceStart
, intervalStart
));
81 long traceEnd
= traceRange
.getEnd();
82 long intervalEndTime
= interval
.getEndTime();
83 double xEnd
= viewer
.timestampToPaneXPos(Math
.min(traceEnd
, intervalEndTime
));
85 double width
= Math
.max(1.0, xEnd
- xStart
) + 1.0;
86 double height
= getHeightFromThickness(interval
.getLineThickness().get());
87 double y
= computeY(height
);
89 xProperty().bind(JfxUtils
.ZERO_PROPERTY
);
95 double opacity
= viewer
.getDebugOptions().stateIntervalOpacity
.get();
100 /* Set initial selection state and selection listener. */
101 if (this.equals(viewer
.getSelectedState())) {
103 viewer
.setSelectedState(this, false);
108 setOnMouseClicked(e
-> {
109 if (e
.getButton() != MouseButton
.PRIMARY
) {
112 viewer
.setSelectedState(this, true);
116 * Initialize the tooltip when the mouse enters the rectangle, if it was
117 * not done previously.
119 setOnMouseEntered(e
-> generateTooltip());
122 private void generateTooltip() {
123 if (fTooltip
!= null) {
126 TooltipContents ttContents
= new TooltipContents(fWidget
.getDebugOptions());
127 ttContents
.addTooltipRow(Messages
.statePropertyElement
, fInterval
.getTreeElement().getName());
128 ttContents
.addTooltipRow(Messages
.statePropertyStateName
, fInterval
.getStateName());
129 ttContents
.addTooltipRow(Messages
.statePropertyStartTime
, fInterval
.getStartTime());
130 ttContents
.addTooltipRow(Messages
.statePropertyEndTime
, fInterval
.getEndTime());
131 ttContents
.addTooltipRow(Messages
.statePropertyDuration
, fInterval
.getDuration() + " ns"); //$NON-NLS-1$
132 /* Add rows corresponding to the properties from the interval */
133 Map
<String
, String
> properties
= fInterval
.getProperties();
134 properties
.forEach((k
, v
) -> ttContents
.addTooltipRow(k
, v
));
136 Tooltip tt
= new Tooltip();
137 tt
.setGraphic(ttContents
);
138 Tooltip
.install(this, tt
);
143 * Return the model interval representing this state
145 * @return The interval model object
147 public TimeGraphStateInterval
getStateInterval() {
151 public void updatePaint() {
152 /* Update the color */
153 /* Set a special paint for multi-state intervals */
154 if (fInterval
.isMultiState()) {
155 Paint multiStatePaint
= fWidget
.getDebugOptions().multiStatePaint
.get();
156 fBaseColor
= multiStatePaint
;
157 fSelectedColor
= multiStatePaint
;
159 fBaseColor
= JfxColorFactory
.getColorFromDef(fInterval
.getColorDefinition().get());
160 fSelectedColor
= JfxColorFactory
.getDerivedColorFromDef(fInterval
.getColorDefinition().get());
164 /* Update the line thickness */
165 LineThickness lt
= fInterval
.getLineThickness().get();
166 double height
= getHeightFromThickness(lt
);
168 /* We need to adjust the y position too */
169 setY(computeY(height
));
173 * Compute the Y property (the Y position of the *top* of the rectangle)
174 * this rectangle should have on its pane. This takes into consideration the
175 * entry it belongs to, as well as its target height.
177 * For example, if the line thickness of the rectangle changes, its Y has to
178 * be recomputed so that the rectangle remains centered on its entry line.
180 * This method does not change the yProperty of the rectangle.
182 private double computeY(double height
) {
183 double yOffset
= (TimeGraphWidget
.ENTRY_HEIGHT
- height
) / 2;
184 double y
= fEntryIndex
* TimeGraphWidget
.ENTRY_HEIGHT
+ yOffset
;
188 public void setSelected(boolean isSelected
) {
190 setFill(fSelectedColor
);
197 public void showTooltip(boolean beginning
) {
199 Tooltip tt
= requireNonNull(fTooltip
);
202 * Show the tooltip first, then move it to the correct location. It
203 * needs to be shown for its getWidth() etc. to be populated.
209 /* Align to the bottom-left of the rectangle, left-aligned. */
210 /* Yes, it needs to be getX() here (0), not getLayoutX(). */
211 position
= this.localToScreen(getX(), getY() + getHeight());
213 /* Align to the bottom-right of the rectangle, right-aligned */
214 position
= this.localToScreen(getX() + getWidth() - tt
.getWidth(), getY() + getHeight());
217 tt
.setAnchorX(position
.getX());
218 tt
.setAnchorY(position
.getY());
221 public void hideTooltip() {
222 Tooltip tt
= fTooltip
;
224 Platform
.runLater(() -> {
231 protected void finalize() {
235 public static double getHeightFromThickness(LineThickness lt
) {
239 return TimeGraphWidget
.ENTRY_HEIGHT
- 4;
241 return TimeGraphWidget
.ENTRY_HEIGHT
- 8;
243 return TimeGraphWidget
.ENTRY_HEIGHT
- 12;
248 public int hashCode() {
249 return Objects
.hash(fWidget
, fInterval
);
253 public boolean equals(@Nullable Object obj
) {
260 if (getClass() != obj
.getClass()) {
263 StateRectangle other
= (StateRectangle
) obj
;
264 return Objects
.equals(fWidget
, other
.fWidget
)
265 && Objects
.equals(fInterval
, other
.fInterval
);
269 public String
toString() {
270 return MoreObjects
.toStringHelper(this)
271 .add("interval", fInterval
) //$NON-NLS-1$
275 private static class TooltipContents
extends CountingGridPane
{
277 private final DebugOptions fOpts
;
279 public TooltipContents(DebugOptions opts
) {
283 public void addTooltipRow(Object
... objects
) {
284 Node
[] labels
= Arrays
.stream(objects
)
285 .map(Object
::toString
)
288 text
.fontProperty().bind(fOpts
.toolTipFont
);
289 text
.fillProperty().bind(fOpts
.toolTipFontFill
);
291 .toArray(Node
[]::new);