Import "views" plugins
[deliverable/tracecompass.git] / tmf / org.lttng.scope.tmf2.views.ui / src / org / lttng / scope / tmf2 / views / ui / timeline / widgets / timegraph / StateRectangle.java
1 /*
2 * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 *
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
8 */
9
10 package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
11
12 import static java.util.Objects.requireNonNull;
13
14 import java.util.Arrays;
15 import java.util.Map;
16 import java.util.Objects;
17
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;
26
27 import com.google.common.base.MoreObjects;
28
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;
37
38 /**
39 * {@link Rectangle} object used to draw states in the timegraph. It attaches
40 * the {@link TimeGraphStateInterval} that represents this state.
41 *
42 * @author Alexandre Montplaisir
43 */
44 public class StateRectangle extends Rectangle {
45
46 private final TimeGraphWidget fWidget;
47 private final int fEntryIndex;
48 private final TimeGraphStateInterval fInterval;
49
50 private @Nullable transient Paint fBaseColor;
51 private @Nullable transient Paint fSelectedColor;
52
53 private @Nullable Tooltip fTooltip = null;
54
55 /**
56 * Constructor
57 *
58 * @param viewer
59 * The viewer in which the rectangle will be placed
60 * @param interval
61 * The source interval model object
62 * @param entryIndex
63 * The index of the entry to which this state belongs.
64 */
65 public StateRectangle(TimeGraphWidget viewer, TimeGraphStateInterval interval, int entryIndex) {
66 fWidget = viewer;
67 fEntryIndex = entryIndex;
68 fInterval = interval;
69
70 /*
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
74 * valid values.
75 */
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));
80
81 long traceEnd = traceRange.getEnd();
82 long intervalEndTime = interval.getEndTime();
83 double xEnd = viewer.timestampToPaneXPos(Math.min(traceEnd, intervalEndTime));
84
85 double width = Math.max(1.0, xEnd - xStart) + 1.0;
86 double height = getHeightFromThickness(interval.getLineThickness().get());
87 double y = computeY(height);
88
89 xProperty().bind(JfxUtils.ZERO_PROPERTY);
90 setLayoutX(xStart);
91 setY(y);
92 setWidth(width);
93 setHeight(height);
94
95 double opacity = viewer.getDebugOptions().stateIntervalOpacity.get();
96 setOpacity(opacity);
97
98 updatePaint();
99
100 /* Set initial selection state and selection listener. */
101 if (this.equals(viewer.getSelectedState())) {
102 setSelected(true);
103 viewer.setSelectedState(this, false);
104 } else {
105 setSelected(false);
106 }
107
108 setOnMouseClicked(e -> {
109 if (e.getButton() != MouseButton.PRIMARY) {
110 return;
111 }
112 viewer.setSelectedState(this, true);
113 });
114
115 /*
116 * Initialize the tooltip when the mouse enters the rectangle, if it was
117 * not done previously.
118 */
119 setOnMouseEntered(e -> generateTooltip());
120 }
121
122 private void generateTooltip() {
123 if (fTooltip != null) {
124 return;
125 }
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));
135
136 Tooltip tt = new Tooltip();
137 tt.setGraphic(ttContents);
138 Tooltip.install(this, tt);
139 fTooltip = tt;
140 }
141
142 /**
143 * Return the model interval representing this state
144 *
145 * @return The interval model object
146 */
147 public TimeGraphStateInterval getStateInterval() {
148 return fInterval;
149 }
150
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;
158 } else {
159 fBaseColor = JfxColorFactory.getColorFromDef(fInterval.getColorDefinition().get());
160 fSelectedColor = JfxColorFactory.getDerivedColorFromDef(fInterval.getColorDefinition().get());
161 }
162 setFill(fBaseColor);
163
164 /* Update the line thickness */
165 LineThickness lt = fInterval.getLineThickness().get();
166 double height = getHeightFromThickness(lt);
167 setHeight(height);
168 /* We need to adjust the y position too */
169 setY(computeY(height));
170 }
171
172 /**
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.
176 *
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.
179 *
180 * This method does not change the yProperty of the rectangle.
181 */
182 private double computeY(double height) {
183 double yOffset = (TimeGraphWidget.ENTRY_HEIGHT - height) / 2;
184 double y = fEntryIndex * TimeGraphWidget.ENTRY_HEIGHT + yOffset;
185 return y;
186 }
187
188 public void setSelected(boolean isSelected) {
189 if (isSelected) {
190 setFill(fSelectedColor);
191 } else {
192 setFill(fBaseColor);
193 hideTooltip();
194 }
195 }
196
197 public void showTooltip(boolean beginning) {
198 generateTooltip();
199 Tooltip tt = requireNonNull(fTooltip);
200
201 /*
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.
204 */
205 tt.show(this, 0, 0);
206
207 Point2D position;
208 if (beginning) {
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());
212 } else {
213 /* Align to the bottom-right of the rectangle, right-aligned */
214 position = this.localToScreen(getX() + getWidth() - tt.getWidth(), getY() + getHeight());
215 }
216
217 tt.setAnchorX(position.getX());
218 tt.setAnchorY(position.getY());
219 }
220
221 public void hideTooltip() {
222 Tooltip tt = fTooltip;
223 if (tt != null) {
224 Platform.runLater(() -> {
225 tt.hide();
226 });
227 }
228 }
229
230 @Override
231 protected void finalize() {
232 hideTooltip();
233 }
234
235 public static double getHeightFromThickness(LineThickness lt) {
236 switch (lt) {
237 case NORMAL:
238 default:
239 return TimeGraphWidget.ENTRY_HEIGHT - 4;
240 case SMALL:
241 return TimeGraphWidget.ENTRY_HEIGHT - 8;
242 case TINY:
243 return TimeGraphWidget.ENTRY_HEIGHT - 12;
244 }
245 }
246
247 @Override
248 public int hashCode() {
249 return Objects.hash(fWidget, fInterval);
250 }
251
252 @Override
253 public boolean equals(@Nullable Object obj) {
254 if (this == obj) {
255 return true;
256 }
257 if (obj == null) {
258 return false;
259 }
260 if (getClass() != obj.getClass()) {
261 return false;
262 }
263 StateRectangle other = (StateRectangle) obj;
264 return Objects.equals(fWidget, other.fWidget)
265 && Objects.equals(fInterval, other.fInterval);
266 }
267
268 @Override
269 public String toString() {
270 return MoreObjects.toStringHelper(this)
271 .add("interval", fInterval) //$NON-NLS-1$
272 .toString();
273 }
274
275 private static class TooltipContents extends CountingGridPane {
276
277 private final DebugOptions fOpts;
278
279 public TooltipContents(DebugOptions opts) {
280 fOpts = opts;
281 }
282
283 public void addTooltipRow(Object... objects) {
284 Node[] labels = Arrays.stream(objects)
285 .map(Object::toString)
286 .map(Text::new)
287 .peek(text -> {
288 text.fontProperty().bind(fOpts.toolTipFont);
289 text.fillProperty().bind(fOpts.toolTipFontFill);
290 })
291 .toArray(Node[]::new);
292 appendRow(labels);
293 }
294 }
295 }
This page took 0.038263 seconds and 5 git commands to generate.