Commit | Line | Data |
---|---|---|
6e512b93 | 1 | /******************************************************************************* |
ed902a2b | 2 | * Copyright (c) 2009, 2015 Ericsson |
1b055dfa | 3 | * |
6e512b93 ASL |
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 | |
1b055dfa | 8 | * |
6e512b93 | 9 | * Contributors: |
b59134e1 | 10 | * William Bourque - Initial API and implementation |
c392540b | 11 | * Yuriy Vashchuk - GUI reorganisation, simplification and some related code improvements. |
1b055dfa | 12 | * Yuriy Vashchuk - Histograms optimisation. |
c392540b FC |
13 | * Yuriy Vashchuk - Histogram Canvas Heritage correction |
14 | * Francois Chouinard - Cleanup and refactoring | |
e0752744 | 15 | * Francois Chouinard - Moved from LTTng to TMF |
65cdf787 | 16 | * Patrick Tasse - Update for mouse wheel zoom |
2fc582d2 | 17 | * Xavier Raynaud - Support multi-trace coloring |
6e512b93 | 18 | *******************************************************************************/ |
3e9fdb8b | 19 | |
2bdf0193 | 20 | package org.eclipse.tracecompass.tmf.ui.views.histogram; |
6e512b93 | 21 | |
c14c0757 GB |
22 | import java.util.Collection; |
23 | ||
dab5f596 | 24 | import org.eclipse.jdt.annotation.NonNull; |
95aa81ef JCK |
25 | import org.eclipse.jface.action.Action; |
26 | import org.eclipse.jface.action.IAction; | |
27 | import org.eclipse.jface.action.Separator; | |
b59134e1 | 28 | import org.eclipse.swt.SWT; |
2fc582d2 | 29 | import org.eclipse.swt.custom.CLabel; |
720d67cb PT |
30 | import org.eclipse.swt.events.MouseAdapter; |
31 | import org.eclipse.swt.events.MouseEvent; | |
65cdf787 | 32 | import org.eclipse.swt.events.MouseWheelListener; |
720d67cb PT |
33 | import org.eclipse.swt.events.PaintEvent; |
34 | import org.eclipse.swt.events.PaintListener; | |
2fc582d2 | 35 | import org.eclipse.swt.graphics.GC; |
720d67cb PT |
36 | import org.eclipse.swt.graphics.Image; |
37 | import org.eclipse.swt.graphics.Rectangle; | |
b59134e1 | 38 | import org.eclipse.swt.layout.GridData; |
252ae4bd | 39 | import org.eclipse.swt.layout.GridLayout; |
2fc582d2 | 40 | import org.eclipse.swt.layout.RowLayout; |
6e512b93 | 41 | import org.eclipse.swt.widgets.Composite; |
2fc582d2 | 42 | import org.eclipse.swt.widgets.Control; |
80c930fa | 43 | import org.eclipse.swt.widgets.Display; |
720d67cb | 44 | import org.eclipse.swt.widgets.Label; |
2bdf0193 AM |
45 | import org.eclipse.tracecompass.internal.tmf.ui.Activator; |
46 | import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants; | |
47 | import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; | |
48 | import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType; | |
97c71024 | 49 | import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal; |
2bdf0193 AM |
50 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; |
51 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalThrottler; | |
97c71024 | 52 | import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal; |
2bdf0193 AM |
53 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; |
54 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; | |
55 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal; | |
56 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; | |
57 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal; | |
58 | import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; | |
59 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; | |
60 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; | |
61 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
21852dfa | 62 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext; |
2bdf0193 AM |
63 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; |
64 | import org.eclipse.tracecompass.tmf.ui.views.TmfView; | |
95aa81ef | 65 | import org.eclipse.ui.IActionBars; |
6e512b93 | 66 | |
544fe9b7 | 67 | /** |
faa38350 | 68 | * The purpose of this view is to provide graphical time distribution statistics about the trace events. |
544fe9b7 | 69 | * <p> |
c392540b FC |
70 | * The view is composed of two histograms and two controls: |
71 | * <ul> | |
faa38350 | 72 | * <li>an event distribution histogram for the whole trace; |
c392540b FC |
73 | * <li>an event distribution histogram for current time window (window span); |
74 | * <li>the timestamp of the currently selected event; | |
75 | * <li>the window span (size of the time window of the smaller histogram). | |
76 | * </ul> | |
77 | * The histograms x-axis show their respective time range. | |
abbdd66a | 78 | * |
f8177ba2 | 79 | * @version 2.0 |
2af7df97 | 80 | * @author Francois Chouinard |
544fe9b7 | 81 | */ |
c392540b FC |
82 | public class HistogramView extends TmfView { |
83 | ||
84 | // ------------------------------------------------------------------------ | |
85 | // Constants | |
86 | // ------------------------------------------------------------------------ | |
87 | ||
b544077e BH |
88 | /** |
89 | * The view ID as defined in plugin.xml | |
90 | */ | |
dab5f596 | 91 | public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.histogram"; //$NON-NLS-1$ |
c392540b | 92 | |
d2e4afa7 | 93 | private static final Image LINK_IMG = Activator.getDefault().getImageFromPath(ITmfImageConstants.IMG_UI_LINK); |
720d67cb | 94 | |
c392540b FC |
95 | // ------------------------------------------------------------------------ |
96 | // Attributes | |
97 | // ------------------------------------------------------------------------ | |
98 | ||
99 | // Parent widget | |
100 | private Composite fParent; | |
101 | ||
faa38350 PT |
102 | // The current trace |
103 | private ITmfTrace fTrace; | |
c392540b | 104 | |
f8177ba2 | 105 | // Current timestamp/time window - everything in the TIME_SCALE |
faa38350 PT |
106 | private long fTraceStartTime; |
107 | private long fTraceEndTime; | |
c392540b FC |
108 | private long fWindowStartTime; |
109 | private long fWindowEndTime; | |
d7ee91bb | 110 | private long fWindowSpan; |
0fcf3b09 PT |
111 | private long fSelectionBeginTime; |
112 | private long fSelectionEndTime; | |
c392540b FC |
113 | |
114 | // Time controls | |
720d67cb PT |
115 | private HistogramTextControl fSelectionStartControl; |
116 | private HistogramTextControl fSelectionEndControl; | |
c392540b FC |
117 | private HistogramTextControl fTimeSpanControl; |
118 | ||
720d67cb PT |
119 | // Link |
120 | private Label fLinkButton; | |
121 | private boolean fLinkState; | |
122 | ||
c392540b FC |
123 | // Histogram/request for the full trace range |
124 | private static FullTraceHistogram fFullTraceHistogram; | |
125 | private HistogramRequest fFullTraceRequest; | |
126 | ||
127 | // Histogram/request for the selected time range | |
128 | private static TimeRangeHistogram fTimeRangeHistogram; | |
129 | private HistogramRequest fTimeRangeRequest; | |
130 | ||
2fc582d2 XR |
131 | // Legend area |
132 | private Composite fLegendArea; | |
133 | private Image[] fLegendImages; | |
134 | ||
38df2c82 AM |
135 | // Throttlers for the time sync and time-range sync signals |
136 | private final TmfSignalThrottler fTimeSyncThrottle; | |
137 | private final TmfSignalThrottler fTimeRangeSyncThrottle; | |
138 | ||
95aa81ef JCK |
139 | // Action for toggle showing the lost events |
140 | private Action hideLostEventsAction; | |
2fc582d2 XR |
141 | // Action for toggle showing the traces |
142 | private Action showTraceAction; | |
95aa81ef | 143 | |
c392540b FC |
144 | // ------------------------------------------------------------------------ |
145 | // Constructor | |
146 | // ------------------------------------------------------------------------ | |
147 | ||
b544077e BH |
148 | /** |
149 | * Default constructor | |
150 | */ | |
c392540b FC |
151 | public HistogramView() { |
152 | super(ID); | |
38df2c82 AM |
153 | fTimeSyncThrottle = new TmfSignalThrottler(this, 200); |
154 | fTimeRangeSyncThrottle = new TmfSignalThrottler(this, 200); | |
c392540b FC |
155 | } |
156 | ||
6a13fa07 | 157 | @Override |
c392540b | 158 | public void dispose() { |
1b055dfa | 159 | if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { |
4dc47e28 FC |
160 | fTimeRangeRequest.cancel(); |
161 | } | |
1b055dfa | 162 | if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { |
4dc47e28 FC |
163 | fFullTraceRequest.cancel(); |
164 | } | |
6a13fa07 FC |
165 | fFullTraceHistogram.dispose(); |
166 | fTimeRangeHistogram.dispose(); | |
720d67cb PT |
167 | fSelectionStartControl.dispose(); |
168 | fSelectionEndControl.dispose(); | |
f8177ba2 | 169 | fTimeSpanControl.dispose(); |
03ec1f6a MAL |
170 | disposeLegendImages(); |
171 | ||
4dc47e28 | 172 | super.dispose(); |
c392540b FC |
173 | } |
174 | ||
03ec1f6a MAL |
175 | private void disposeLegendImages() { |
176 | if (fLegendImages != null) { | |
177 | for (Image i: fLegendImages) { | |
178 | i.dispose(); | |
179 | } | |
180 | } | |
181 | fLegendImages = null; | |
182 | } | |
183 | ||
c392540b FC |
184 | // ------------------------------------------------------------------------ |
185 | // TmfView | |
186 | // ------------------------------------------------------------------------ | |
187 | ||
188 | @Override | |
c392540b FC |
189 | public void createPartControl(Composite parent) { |
190 | ||
191 | fParent = parent; | |
192 | ||
193 | // Control labels | |
720d67cb PT |
194 | final String selectionStartLabel = Messages.HistogramView_selectionStartLabel; |
195 | final String selectionEndLabel = Messages.HistogramView_selectionEndLabel; | |
c392540b FC |
196 | final String windowSpanLabel = Messages.HistogramView_windowSpanLabel; |
197 | ||
198 | // -------------------------------------------------------------------- | |
199 | // Set the HistogramView layout | |
200 | // -------------------------------------------------------------------- | |
201 | ||
202 | Composite viewComposite = new Composite(fParent, SWT.FILL); | |
203 | GridLayout gridLayout = new GridLayout(); | |
204 | gridLayout.numColumns = 2; | |
205 | gridLayout.horizontalSpacing = 5; | |
206 | gridLayout.verticalSpacing = 0; | |
207 | gridLayout.marginHeight = 0; | |
208 | gridLayout.marginWidth = 0; | |
209 | viewComposite.setLayout(gridLayout); | |
210 | ||
c392540b FC |
211 | // -------------------------------------------------------------------- |
212 | // Time controls | |
213 | // -------------------------------------------------------------------- | |
214 | ||
720d67cb | 215 | Composite controlsComposite = new Composite(viewComposite, SWT.NONE); |
c392540b FC |
216 | gridLayout = new GridLayout(); |
217 | gridLayout.numColumns = 2; | |
218 | gridLayout.marginHeight = 0; | |
219 | gridLayout.marginWidth = 0; | |
220 | gridLayout.horizontalSpacing = 5; | |
720d67cb | 221 | gridLayout.verticalSpacing = 1; |
f8177ba2 | 222 | gridLayout.makeColumnsEqualWidth = false; |
c392540b | 223 | controlsComposite.setLayout(gridLayout); |
720d67cb PT |
224 | GridData gridData = new GridData(SWT.FILL, SWT.CENTER, false, false); |
225 | controlsComposite.setLayoutData(gridData); | |
226 | ||
227 | Composite selectionGroup = new Composite(controlsComposite, SWT.BORDER); | |
228 | gridLayout = new GridLayout(); | |
229 | gridLayout.marginHeight = 0; | |
230 | gridLayout.marginWidth = 0; | |
231 | gridLayout.horizontalSpacing = 0; | |
232 | gridLayout.verticalSpacing = 0; | |
233 | selectionGroup.setLayout(gridLayout); | |
c392540b | 234 | |
720d67cb | 235 | // Selection start control |
c392540b | 236 | gridData = new GridData(); |
720d67cb PT |
237 | gridData.horizontalAlignment = SWT.FILL; |
238 | gridData.verticalAlignment = SWT.CENTER; | |
239 | fSelectionStartControl = new HistogramSelectionStartControl(this, selectionGroup, selectionStartLabel, 0L); | |
240 | fSelectionStartControl.setLayoutData(gridData); | |
241 | fSelectionStartControl.setValue(Long.MIN_VALUE); | |
242 | ||
243 | // Selection end control | |
244 | gridData = new GridData(); | |
245 | gridData.horizontalAlignment = SWT.FILL; | |
c392540b | 246 | gridData.verticalAlignment = SWT.CENTER; |
720d67cb PT |
247 | fSelectionEndControl = new HistogramSelectionEndControl(this, selectionGroup, selectionEndLabel, 0L); |
248 | fSelectionEndControl.setLayoutData(gridData); | |
249 | fSelectionEndControl.setValue(Long.MIN_VALUE); | |
250 | ||
251 | // Link button | |
252 | gridData = new GridData(); | |
253 | fLinkButton = new Label(controlsComposite, SWT.NONE); | |
254 | fLinkButton.setImage(LINK_IMG); | |
255 | fLinkButton.setLayoutData(gridData); | |
256 | addLinkButtonListeners(); | |
c392540b FC |
257 | |
258 | // Window span time control | |
259 | gridData = new GridData(); | |
720d67cb | 260 | gridData.horizontalAlignment = SWT.FILL; |
c392540b | 261 | gridData.verticalAlignment = SWT.CENTER; |
f8177ba2 | 262 | fTimeSpanControl = new HistogramTimeRangeControl(this, controlsComposite, windowSpanLabel, 0L); |
c392540b | 263 | fTimeSpanControl.setLayoutData(gridData); |
da7bdcbc | 264 | fTimeSpanControl.setValue(Long.MIN_VALUE); |
c392540b FC |
265 | |
266 | // -------------------------------------------------------------------- | |
267 | // Time range histogram | |
268 | // -------------------------------------------------------------------- | |
269 | ||
720d67cb | 270 | Composite timeRangeComposite = new Composite(viewComposite, SWT.NONE); |
c392540b FC |
271 | gridLayout = new GridLayout(); |
272 | gridLayout.numColumns = 1; | |
273 | gridLayout.marginHeight = 0; | |
274 | gridLayout.marginWidth = 0; | |
275 | gridLayout.marginTop = 5; | |
276 | gridLayout.horizontalSpacing = 0; | |
277 | gridLayout.verticalSpacing = 0; | |
278 | gridLayout.marginLeft = 5; | |
279 | gridLayout.marginRight = 5; | |
280 | timeRangeComposite.setLayout(gridLayout); | |
281 | ||
282 | // Use remaining horizontal space | |
283 | gridData = new GridData(); | |
284 | gridData.horizontalAlignment = SWT.FILL; | |
285 | gridData.verticalAlignment = SWT.FILL; | |
286 | gridData.grabExcessHorizontalSpace = true; | |
3a790c10 | 287 | gridData.grabExcessVerticalSpace = true; |
c392540b FC |
288 | timeRangeComposite.setLayoutData(gridData); |
289 | ||
290 | // Histogram | |
291 | fTimeRangeHistogram = new TimeRangeHistogram(this, timeRangeComposite); | |
292 | ||
293 | // -------------------------------------------------------------------- | |
294 | // Full range histogram | |
295 | // -------------------------------------------------------------------- | |
296 | ||
297 | Composite fullRangeComposite = new Composite(viewComposite, SWT.FILL); | |
298 | gridLayout = new GridLayout(); | |
299 | gridLayout.numColumns = 1; | |
300 | gridLayout.marginHeight = 0; | |
301 | gridLayout.marginWidth = 0; | |
302 | gridLayout.marginTop = 5; | |
303 | gridLayout.horizontalSpacing = 0; | |
304 | gridLayout.verticalSpacing = 0; | |
305 | gridLayout.marginLeft = 5; | |
306 | gridLayout.marginRight = 5; | |
307 | fullRangeComposite.setLayout(gridLayout); | |
308 | ||
309 | // Use remaining horizontal space | |
310 | gridData = new GridData(); | |
311 | gridData.horizontalAlignment = SWT.FILL; | |
312 | gridData.verticalAlignment = SWT.FILL; | |
313 | gridData.horizontalSpan = 2; | |
314 | gridData.grabExcessHorizontalSpace = true; | |
3a790c10 | 315 | gridData.grabExcessVerticalSpace = true; |
c392540b FC |
316 | fullRangeComposite.setLayoutData(gridData); |
317 | ||
318 | // Histogram | |
319 | fFullTraceHistogram = new FullTraceHistogram(this, fullRangeComposite); | |
320 | ||
2fc582d2 XR |
321 | fLegendArea = new Composite(viewComposite, SWT.FILL); |
322 | fLegendArea.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, true, false, 2, 1)); | |
323 | fLegendArea.setLayout(new RowLayout()); | |
324 | ||
65cdf787 PT |
325 | // Add mouse wheel listener to time span control |
326 | MouseWheelListener listener = fFullTraceHistogram.getZoom(); | |
327 | fTimeSpanControl.addMouseWheelListener(listener); | |
328 | ||
95aa81ef JCK |
329 | |
330 | // View Action Handling | |
331 | contributeToActionBars(); | |
332 | ||
21852dfa | 333 | ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace(); |
3ac5721a AM |
334 | if (trace != null) { |
335 | traceSelected(new TmfTraceSelectedSignal(this, trace)); | |
1b055dfa | 336 | } |
ecfd1d41 | 337 | } |
c392540b FC |
338 | |
339 | @Override | |
c392540b | 340 | public void setFocus() { |
faa38350 | 341 | fFullTraceHistogram.fCanvas.setFocus(); |
833a21aa | 342 | } |
c392540b | 343 | |
f8177ba2 FC |
344 | void refresh() { |
345 | fParent.layout(true); | |
346 | } | |
347 | ||
c392540b FC |
348 | // ------------------------------------------------------------------------ |
349 | // Accessors | |
350 | // ------------------------------------------------------------------------ | |
351 | ||
faa38350 PT |
352 | /** |
353 | * Returns the current trace handled by the view | |
354 | * | |
355 | * @return the current trace | |
faa38350 PT |
356 | */ |
357 | public ITmfTrace getTrace() { | |
358 | return fTrace; | |
359 | } | |
360 | ||
b544077e BH |
361 | /** |
362 | * Returns the time range of the current selected window (base on default time scale). | |
abbdd66a | 363 | * |
b544077e BH |
364 | * @return the time range of current selected window. |
365 | */ | |
c392540b | 366 | public TmfTimeRange getTimeRange() { |
f8177ba2 FC |
367 | return new TmfTimeRange( |
368 | new TmfTimestamp(fWindowStartTime, ITmfTimestamp.NANOSECOND_SCALE), | |
369 | new TmfTimestamp(fWindowEndTime, ITmfTimestamp.NANOSECOND_SCALE)); | |
c392540b FC |
370 | } |
371 | ||
95aa81ef JCK |
372 | /** |
373 | * get the show lost events action | |
374 | * | |
375 | * @return The action object | |
95aa81ef JCK |
376 | */ |
377 | public Action getShowLostEventsAction() { | |
378 | if (hideLostEventsAction == null) { | |
379 | /* show lost events */ | |
380 | hideLostEventsAction = new Action(Messages.HistogramView_hideLostEvents, IAction.AS_CHECK_BOX) { | |
381 | @Override | |
382 | public void run() { | |
383 | HistogramScaledData.hideLostEvents = hideLostEventsAction.isChecked(); | |
384 | long maxNbEvents = HistogramScaledData.hideLostEvents ? fFullTraceHistogram.fScaledData.fMaxValue : fFullTraceHistogram.fScaledData.fMaxCombinedValue; | |
3311a6ca | 385 | fFullTraceHistogram.setMaxNbEvents(maxNbEvents); |
95aa81ef | 386 | maxNbEvents = HistogramScaledData.hideLostEvents ? fTimeRangeHistogram.fScaledData.fMaxValue : fTimeRangeHistogram.fScaledData.fMaxCombinedValue; |
3311a6ca | 387 | fTimeRangeHistogram.setMaxNbEvents(maxNbEvents); |
95aa81ef JCK |
388 | } |
389 | }; | |
390 | hideLostEventsAction.setText(Messages.HistogramView_hideLostEvents); | |
391 | hideLostEventsAction.setToolTipText(Messages.HistogramView_hideLostEvents); | |
392 | hideLostEventsAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LOST_EVENTS)); | |
393 | } | |
394 | return hideLostEventsAction; | |
395 | } | |
396 | ||
2fc582d2 XR |
397 | /** |
398 | * get the show trace action | |
399 | * | |
400 | * @return The action object | |
2fc582d2 XR |
401 | */ |
402 | public Action getShowTraceAction() { | |
403 | if (showTraceAction == null) { | |
404 | /* show lost events */ | |
405 | showTraceAction = new Action(Messages.HistogramView_showTraces, IAction.AS_CHECK_BOX) { | |
406 | @Override | |
407 | public void run() { | |
408 | Histogram.showTraces = showTraceAction.isChecked(); | |
409 | fFullTraceHistogram.fCanvas.redraw(); | |
410 | fTimeRangeHistogram.fCanvas.redraw(); | |
411 | updateLegendArea(); | |
412 | } | |
413 | }; | |
414 | showTraceAction.setChecked(true); | |
415 | showTraceAction.setText(Messages.HistogramView_showTraces); | |
416 | showTraceAction.setToolTipText(Messages.HistogramView_showTraces); | |
417 | showTraceAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_HIST_TRACES)); | |
418 | } | |
419 | return showTraceAction; | |
420 | } | |
421 | ||
c392540b FC |
422 | // ------------------------------------------------------------------------ |
423 | // Operations | |
424 | // ------------------------------------------------------------------------ | |
425 | ||
b544077e | 426 | /** |
0fcf3b09 PT |
427 | * Broadcast TmfSignal about new current selection time range. |
428 | * @param beginTime the begin time of current selection. | |
429 | * @param endTime the end time of current selection. | |
b544077e | 430 | */ |
0fcf3b09 PT |
431 | void updateSelectionTime(long beginTime, long endTime) { |
432 | updateDisplayedSelectionTime(beginTime, endTime); | |
433 | TmfTimestamp beginTs = new TmfTimestamp(beginTime, ITmfTimestamp.NANOSECOND_SCALE); | |
434 | TmfTimestamp endTs = new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE); | |
97c71024 | 435 | TmfSelectionRangeUpdatedSignal signal = new TmfSelectionRangeUpdatedSignal(this, beginTs, endTs); |
0fcf3b09 | 436 | fTimeSyncThrottle.queue(signal); |
c392540b FC |
437 | } |
438 | ||
720d67cb PT |
439 | /** |
440 | * Get selection begin time | |
441 | * @return the begin time of current selection | |
442 | */ | |
443 | long getSelectionBegin() { | |
444 | return fSelectionBeginTime; | |
445 | } | |
446 | ||
447 | /** | |
448 | * Get selection end time | |
449 | * @return the end time of current selection | |
450 | */ | |
451 | long getSelectionEnd() { | |
452 | return fSelectionEndTime; | |
453 | } | |
454 | ||
455 | /** | |
456 | * Get the link state | |
457 | * @return true if begin and end selection time should be linked | |
458 | */ | |
459 | boolean getLinkState() { | |
460 | return fLinkState; | |
461 | } | |
462 | ||
b544077e | 463 | /** |
0fcf3b09 | 464 | * Broadcast TmfSignal about new selection time range. |
b544077e BH |
465 | * @param startTime the new start time |
466 | * @param endTime the new end time | |
467 | */ | |
f8177ba2 | 468 | void updateTimeRange(long startTime, long endTime) { |
faa38350 | 469 | if (fTrace != null) { |
c392540b | 470 | // Build the new time range; keep the current time |
f8177ba2 FC |
471 | TmfTimeRange timeRange = new TmfTimeRange( |
472 | new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE), | |
473 | new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE)); | |
c392540b FC |
474 | fTimeSpanControl.setValue(endTime - startTime); |
475 | ||
38df2c82 AM |
476 | updateDisplayedTimeRange(startTime, endTime); |
477 | ||
c392540b | 478 | // Send the FW signal |
97c71024 | 479 | TmfWindowRangeUpdatedSignal signal = new TmfWindowRangeUpdatedSignal(this, timeRange); |
38df2c82 | 480 | fTimeRangeSyncThrottle.queue(signal); |
c392540b FC |
481 | } |
482 | } | |
483 | ||
b544077e BH |
484 | /** |
485 | * Broadcast TmfSignal about new selected time range. | |
486 | * @param newDuration new duration (relative to current start time) | |
487 | */ | |
c392540b | 488 | public synchronized void updateTimeRange(long newDuration) { |
faa38350 | 489 | if (fTrace != null) { |
6a13fa07 | 490 | long delta = newDuration - fWindowSpan; |
1c6a842a | 491 | long newStartTime = fWindowStartTime - (delta / 2); |
6a13fa07 | 492 | setNewRange(newStartTime, newDuration); |
c392540b FC |
493 | } |
494 | } | |
495 | ||
496 | private void setNewRange(long startTime, long duration) { | |
41b5c37f AM |
497 | long realStart = startTime; |
498 | ||
499 | if (realStart < fTraceStartTime) { | |
500 | realStart = fTraceStartTime; | |
1b055dfa | 501 | } |
c392540b | 502 | |
41b5c37f | 503 | long endTime = realStart + duration; |
faa38350 PT |
504 | if (endTime > fTraceEndTime) { |
505 | endTime = fTraceEndTime; | |
1c6a842a | 506 | if ((endTime - duration) > fTraceStartTime) { |
41b5c37f | 507 | realStart = endTime - duration; |
1b055dfa | 508 | } else { |
41b5c37f | 509 | realStart = fTraceStartTime; |
6a13fa07 | 510 | } |
c392540b | 511 | } |
41b5c37f | 512 | updateTimeRange(realStart, endTime); |
833a21aa | 513 | } |
c392540b FC |
514 | |
515 | // ------------------------------------------------------------------------ | |
516 | // Signal handlers | |
517 | // ------------------------------------------------------------------------ | |
518 | ||
b544077e | 519 | /** |
faa38350 | 520 | * Handles trace opened signal. Loads histogram if new trace time range is not |
b544077e | 521 | * equal <code>TmfTimeRange.NULL_RANGE</code> |
fec1ac0b | 522 | * @param signal the trace opened signal |
b544077e | 523 | */ |
1406f802 | 524 | @TmfSignalHandler |
faa38350 | 525 | public void traceOpened(TmfTraceOpenedSignal signal) { |
c392540b | 526 | assert (signal != null); |
faa38350 PT |
527 | fTrace = signal.getTrace(); |
528 | loadTrace(); | |
ecfd1d41 | 529 | } |
550d787e | 530 | |
faa38350 PT |
531 | /** |
532 | * Handles trace selected signal. Loads histogram if new trace time range is not | |
533 | * equal <code>TmfTimeRange.NULL_RANGE</code> | |
534 | * @param signal the trace selected signal | |
faa38350 PT |
535 | */ |
536 | @TmfSignalHandler | |
537 | public void traceSelected(TmfTraceSelectedSignal signal) { | |
538 | assert (signal != null); | |
539 | if (fTrace != signal.getTrace()) { | |
540 | fTrace = signal.getTrace(); | |
541 | loadTrace(); | |
542 | } | |
543 | } | |
544 | ||
545 | private void loadTrace() { | |
c392540b FC |
546 | initializeHistograms(); |
547 | fParent.redraw(); | |
550d787e FC |
548 | } |
549 | ||
ea279a69 | 550 | /** |
faa38350 PT |
551 | * Handles trace closed signal. Clears the view and data model and cancels requests. |
552 | * @param signal the trace closed signal | |
ea279a69 FC |
553 | */ |
554 | @TmfSignalHandler | |
faa38350 PT |
555 | public void traceClosed(TmfTraceClosedSignal signal) { |
556 | ||
557 | if (signal.getTrace() != fTrace) { | |
558 | return; | |
559 | } | |
ea279a69 FC |
560 | |
561 | // Kill any running request | |
562 | if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { | |
563 | fTimeRangeRequest.cancel(); | |
564 | } | |
565 | if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { | |
566 | fFullTraceRequest.cancel(); | |
567 | } | |
568 | ||
569 | // Initialize the internal data | |
faa38350 PT |
570 | fTrace = null; |
571 | fTraceStartTime = 0L; | |
572 | fTraceEndTime = 0L; | |
f8177ba2 FC |
573 | fWindowStartTime = 0L; |
574 | fWindowEndTime = 0L; | |
d7ee91bb | 575 | fWindowSpan = 0L; |
0fcf3b09 PT |
576 | fSelectionBeginTime = 0L; |
577 | fSelectionEndTime = 0L; | |
ea279a69 FC |
578 | |
579 | // Clear the UI widgets | |
580 | fFullTraceHistogram.clear(); | |
581 | fTimeRangeHistogram.clear(); | |
720d67cb PT |
582 | fSelectionStartControl.setValue(Long.MIN_VALUE); |
583 | fSelectionEndControl.setValue(Long.MIN_VALUE); | |
f8177ba2 | 584 | |
da7bdcbc | 585 | fTimeSpanControl.setValue(Long.MIN_VALUE); |
2fc582d2 XR |
586 | |
587 | for (Control c: fLegendArea.getChildren()) { | |
588 | c.dispose(); | |
589 | } | |
03ec1f6a | 590 | disposeLegendImages(); |
2fc582d2 XR |
591 | fLegendArea.layout(); |
592 | fLegendArea.getParent().layout(); | |
ea279a69 FC |
593 | } |
594 | ||
b544077e | 595 | /** |
faa38350 | 596 | * Handles trace range updated signal. Extends histogram according to the new time range. If a |
b544077e BH |
597 | * HistogramRequest is already ongoing, it will be cancelled and a new request with the new range |
598 | * will be issued. | |
abbdd66a | 599 | * |
faa38350 | 600 | * @param signal the trace range updated signal |
b544077e | 601 | */ |
74237cc3 | 602 | @TmfSignalHandler |
faa38350 | 603 | public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) { |
74237cc3 | 604 | |
faa38350 | 605 | if (signal.getTrace() != fTrace) { |
09d11238 PT |
606 | return; |
607 | } | |
608 | ||
74237cc3 FC |
609 | TmfTimeRange fullRange = signal.getRange(); |
610 | ||
faa38350 PT |
611 | fTraceStartTime = fullRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); |
612 | fTraceEndTime = fullRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
74237cc3 | 613 | |
faa38350 PT |
614 | fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime); |
615 | fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); | |
74237cc3 | 616 | |
74237cc3 FC |
617 | sendFullRangeRequest(fullRange); |
618 | } | |
c392540b | 619 | |
b544077e | 620 | /** |
faa38350 PT |
621 | * Handles the trace updated signal. Used to update time limits (start and end time) |
622 | * @param signal the trace updated signal | |
b544077e | 623 | */ |
a28d503d | 624 | @TmfSignalHandler |
faa38350 PT |
625 | public void traceUpdated(TmfTraceUpdatedSignal signal) { |
626 | if (signal.getTrace() != fTrace) { | |
a28d503d PT |
627 | return; |
628 | } | |
faa38350 PT |
629 | TmfTimeRange fullRange = signal.getTrace().getTimeRange(); |
630 | fTraceStartTime = fullRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
631 | fTraceEndTime = fullRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
a28d503d | 632 | |
faa38350 PT |
633 | fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime); |
634 | fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); | |
635 | ||
636 | if ((fFullTraceRequest != null) && fFullTraceRequest.getRange().getEndTime().compareTo(signal.getRange().getEndTime()) < 0) { | |
637 | sendFullRangeRequest(fullRange); | |
638 | } | |
639 | } | |
a28d503d | 640 | |
b544077e | 641 | /** |
97c71024 AM |
642 | * Handles the selection range updated signal. Sets the current time |
643 | * selection in the time range histogram as well as the full histogram. | |
abbdd66a | 644 | * |
97c71024 AM |
645 | * @param signal |
646 | * the signal to process | |
647 | * @since 1.0 | |
b544077e | 648 | */ |
c392540b | 649 | @TmfSignalHandler |
97c71024 | 650 | public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) { |
80c930fa PT |
651 | if (Display.getCurrent() == null) { |
652 | // Make sure the signal is handled in the UI thread | |
653 | Display.getDefault().asyncExec(new Runnable() { | |
654 | @Override | |
655 | public void run() { | |
656 | if (fParent.isDisposed()) { | |
657 | return; | |
658 | } | |
97c71024 | 659 | selectionRangeUpdated(signal); |
80c930fa PT |
660 | } |
661 | }); | |
662 | return; | |
663 | } | |
c392540b | 664 | |
0fcf3b09 PT |
665 | // Update the selected time range |
666 | ITmfTimestamp beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE); | |
667 | ITmfTimestamp endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE); | |
668 | updateDisplayedSelectionTime(beginTime.getValue(), endTime.getValue()); | |
ecfd1d41 | 669 | } |
f05aabed | 670 | |
b544077e | 671 | /** |
97c71024 AM |
672 | * Updates the current window time range in the time range histogram and |
673 | * full range histogram. | |
674 | * | |
675 | * @param signal | |
676 | * the signal to process | |
677 | * @since 1.0 | |
b544077e | 678 | */ |
c392540b | 679 | @TmfSignalHandler |
97c71024 | 680 | public void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal) { |
80c930fa PT |
681 | if (Display.getCurrent() == null) { |
682 | // Make sure the signal is handled in the UI thread | |
683 | Display.getDefault().asyncExec(new Runnable() { | |
684 | @Override | |
685 | public void run() { | |
686 | if (fParent.isDisposed()) { | |
687 | return; | |
688 | } | |
97c71024 | 689 | windowRangeUpdated(signal); |
80c930fa PT |
690 | } |
691 | }); | |
692 | return; | |
693 | } | |
c392540b | 694 | |
faa38350 | 695 | if (fTrace != null) { |
1c6a842a PT |
696 | // Validate the time range |
697 | TmfTimeRange range = signal.getCurrentRange().getIntersection(fTrace.getTimeRange()); | |
698 | if (range == null) { | |
699 | return; | |
700 | } | |
701 | ||
38df2c82 AM |
702 | updateDisplayedTimeRange( |
703 | range.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(), | |
704 | range.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue()); | |
c392540b | 705 | |
38df2c82 | 706 | // Send the event request to populate the small histogram |
c392540b | 707 | sendTimeRangeRequest(fWindowStartTime, fWindowEndTime); |
f8177ba2 | 708 | |
c392540b FC |
709 | fTimeSpanControl.setValue(fWindowSpan); |
710 | } | |
b59134e1 | 711 | } |
c392540b FC |
712 | |
713 | // ------------------------------------------------------------------------ | |
714 | // Helper functions | |
715 | // ------------------------------------------------------------------------ | |
716 | ||
717 | private void initializeHistograms() { | |
faa38350 | 718 | TmfTimeRange fullRange = updateTraceTimeRange(); |
21852dfa AM |
719 | |
720 | TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext(); | |
721 | long selectionBeginTime = ctx.getSelectionRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
722 | long selectionEndTime = ctx.getSelectionRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
723 | long startTime = ctx.getWindowRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
724 | long duration = ctx.getWindowRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue() - startTime; | |
74237cc3 | 725 | |
faa38350 PT |
726 | if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { |
727 | fTimeRangeRequest.cancel(); | |
728 | } | |
74237cc3 | 729 | fTimeRangeHistogram.clear(); |
faa38350 | 730 | fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); |
d7ee91bb | 731 | fTimeRangeHistogram.setTimeRange(startTime, duration); |
0fcf3b09 | 732 | fTimeRangeHistogram.setSelection(selectionBeginTime, selectionEndTime); |
2fc582d2 | 733 | fTimeRangeHistogram.fDataModel.setTrace(fTrace); |
c392540b | 734 | |
faa38350 PT |
735 | if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { |
736 | fFullTraceRequest.cancel(); | |
737 | } | |
74237cc3 | 738 | fFullTraceHistogram.clear(); |
faa38350 | 739 | fFullTraceHistogram.setFullRange(fTraceStartTime, fTraceEndTime); |
d7ee91bb | 740 | fFullTraceHistogram.setTimeRange(startTime, duration); |
0fcf3b09 | 741 | fFullTraceHistogram.setSelection(selectionBeginTime, selectionEndTime); |
2fc582d2 | 742 | fFullTraceHistogram.fDataModel.setTrace(fTrace); |
c392540b | 743 | |
d7ee91bb PT |
744 | fWindowStartTime = startTime; |
745 | fWindowSpan = duration; | |
746 | fWindowEndTime = startTime + duration; | |
6a13fa07 | 747 | |
0fcf3b09 PT |
748 | fSelectionBeginTime = selectionBeginTime; |
749 | fSelectionEndTime = selectionEndTime; | |
720d67cb PT |
750 | fSelectionStartControl.setValue(fSelectionBeginTime); |
751 | fSelectionEndControl.setValue(fSelectionEndTime); | |
f8177ba2 | 752 | |
d7ee91bb | 753 | fTimeSpanControl.setValue(duration); |
6a13fa07 | 754 | |
c14c0757 GB |
755 | Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(fTrace); |
756 | if (!traces.isEmpty()) { | |
757 | this.showTraceAction.setEnabled(traces.size() < fFullTraceHistogram.getMaxNbTraces()); | |
2fc582d2 XR |
758 | } |
759 | updateLegendArea(); | |
760 | ||
2af7df97 | 761 | if (!fullRange.equals(TmfTimeRange.NULL_RANGE)) { |
d7ee91bb | 762 | sendTimeRangeRequest(startTime, startTime + duration); |
2af7df97 FC |
763 | sendFullRangeRequest(fullRange); |
764 | } | |
74237cc3 FC |
765 | } |
766 | ||
2fc582d2 XR |
767 | private void updateLegendArea() { |
768 | for (Control c: fLegendArea.getChildren()) { | |
769 | c.dispose(); | |
770 | } | |
03ec1f6a | 771 | disposeLegendImages(); |
2fc582d2 | 772 | if (fFullTraceHistogram.showTraces()) { |
c14c0757 GB |
773 | Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(fTrace); |
774 | fLegendImages = new Image[traces.size()]; | |
2fc582d2 XR |
775 | int traceIndex = 0; |
776 | for (ITmfTrace trace : traces) { | |
777 | fLegendImages[traceIndex] = new Image(fLegendArea.getDisplay(), 16, 16); | |
778 | GC gc = new GC(fLegendImages[traceIndex]); | |
779 | gc.setBackground(fFullTraceHistogram.getTraceColor(traceIndex)); | |
780 | gc.fillRectangle(0, 0, 15, 15); | |
781 | gc.setForeground(fLegendArea.getDisplay().getSystemColor(SWT.COLOR_BLACK)); | |
782 | gc.drawRectangle(0, 0, 15, 15); | |
783 | gc.dispose(); | |
784 | ||
785 | CLabel label = new CLabel(fLegendArea, SWT.NONE); | |
786 | label.setText(trace.getName()); | |
787 | label.setImage(fLegendImages[traceIndex]); | |
788 | traceIndex++; | |
789 | } | |
790 | } | |
791 | fLegendArea.layout(); | |
792 | fLegendArea.getParent().layout(); | |
793 | } | |
794 | ||
0fcf3b09 PT |
795 | private void updateDisplayedSelectionTime(long beginTime, long endTime) { |
796 | fSelectionBeginTime = beginTime; | |
797 | fSelectionEndTime = endTime; | |
38df2c82 | 798 | |
0fcf3b09 PT |
799 | fFullTraceHistogram.setSelection(fSelectionBeginTime, fSelectionEndTime); |
800 | fTimeRangeHistogram.setSelection(fSelectionBeginTime, fSelectionEndTime); | |
720d67cb PT |
801 | fSelectionStartControl.setValue(fSelectionBeginTime); |
802 | fSelectionEndControl.setValue(fSelectionEndTime); | |
38df2c82 AM |
803 | } |
804 | ||
805 | private void updateDisplayedTimeRange(long start, long end) { | |
806 | fWindowStartTime = start; | |
807 | fWindowEndTime = end; | |
808 | fWindowSpan = fWindowEndTime - fWindowStartTime; | |
809 | fFullTraceHistogram.setTimeRange(fWindowStartTime, fWindowSpan); | |
810 | } | |
811 | ||
faa38350 PT |
812 | private TmfTimeRange updateTraceTimeRange() { |
813 | fTraceStartTime = 0L; | |
814 | fTraceEndTime = 0L; | |
74237cc3 | 815 | |
faa38350 | 816 | TmfTimeRange timeRange = fTrace.getTimeRange(); |
c6023803 | 817 | if (!timeRange.equals(TmfTimeRange.NULL_RANGE)) { |
faa38350 PT |
818 | fTraceStartTime = timeRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); |
819 | fTraceEndTime = timeRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(); | |
74237cc3 FC |
820 | } |
821 | return timeRange; | |
b59134e1 | 822 | } |
c392540b FC |
823 | |
824 | private void sendTimeRangeRequest(long startTime, long endTime) { | |
1b055dfa | 825 | if ((fTimeRangeRequest != null) && !fTimeRangeRequest.isCompleted()) { |
c392540b | 826 | fTimeRangeRequest.cancel(); |
088c1d4e | 827 | } |
f8177ba2 FC |
828 | TmfTimestamp startTS = new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE); |
829 | TmfTimestamp endTS = new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE); | |
c392540b FC |
830 | TmfTimeRange timeRange = new TmfTimeRange(startTS, endTS); |
831 | ||
832 | fTimeRangeHistogram.clear(); | |
15844a4e | 833 | fTimeRangeHistogram.setFullRange(fTraceStartTime, fTraceEndTime); |
c392540b | 834 | fTimeRangeHistogram.setTimeRange(startTime, endTime - startTime); |
4cf201de | 835 | |
faa38350 | 836 | int cacheSize = fTrace.getCacheSize(); |
2740e05c AM |
837 | fTimeRangeRequest = new HistogramRequest(fTimeRangeHistogram.getDataModel(), |
838 | timeRange, 0, ITmfEventRequest.ALL_DATA, cacheSize, ExecutionType.FOREGROUND, false); | |
faa38350 | 839 | fTrace.sendRequest(fTimeRangeRequest); |
088c1d4e | 840 | } |
c392540b | 841 | |
74237cc3 | 842 | private void sendFullRangeRequest(TmfTimeRange fullRange) { |
1b055dfa | 843 | if ((fFullTraceRequest != null) && !fFullTraceRequest.isCompleted()) { |
c392540b FC |
844 | fFullTraceRequest.cancel(); |
845 | } | |
faa38350 | 846 | int cacheSize = fTrace.getCacheSize(); |
2740e05c AM |
847 | fFullTraceRequest = new HistogramRequest(fFullTraceHistogram.getDataModel(), |
848 | fullRange, | |
849 | (int) fFullTraceHistogram.fDataModel.getNbEvents(), | |
850 | ITmfEventRequest.ALL_DATA, | |
851 | cacheSize, | |
852 | ExecutionType.BACKGROUND, true); | |
faa38350 | 853 | fTrace.sendRequest(fFullTraceRequest); |
ed4b3b9f | 854 | } |
c392540b | 855 | |
95aa81ef JCK |
856 | private void contributeToActionBars() { |
857 | IActionBars bars = getViewSite().getActionBars(); | |
858 | bars.getToolBarManager().add(getShowLostEventsAction()); | |
2fc582d2 | 859 | bars.getToolBarManager().add(getShowTraceAction()); |
95aa81ef JCK |
860 | bars.getToolBarManager().add(new Separator()); |
861 | } | |
862 | ||
720d67cb PT |
863 | private void addLinkButtonListeners() { |
864 | fLinkButton.addMouseListener(new MouseAdapter() { | |
865 | @Override | |
866 | public void mouseDown(MouseEvent e) { | |
867 | fSelectionEndControl.setEnabled(fLinkState); | |
868 | fLinkState = !fLinkState; | |
869 | fLinkButton.redraw(); | |
870 | } | |
871 | }); | |
872 | ||
873 | fLinkButton.addPaintListener(new PaintListener() { | |
874 | @Override | |
875 | public void paintControl(PaintEvent e) { | |
876 | if (fLinkState) { | |
877 | Rectangle r = fLinkButton.getBounds(); | |
878 | r.x = -1; | |
879 | r.y = -1; | |
880 | e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); | |
881 | e.gc.drawRectangle(r); | |
882 | r.x = 0; | |
883 | r.y = 0; | |
884 | e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_DARK_GRAY)); | |
885 | e.gc.drawRectangle(r); | |
886 | } | |
887 | } | |
888 | }); | |
889 | } | |
6e512b93 | 890 | } |