Commit | Line | Data |
---|---|---|
03c96217 | 1 | /****************************************************************************** |
edded5c1 | 2 | * Copyright (c) 2015, 2016 Ericsson |
03c96217 BH |
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 | * Contributors: | |
10 | * France Lapointe Nguyen - Initial API and implementation | |
11 | * Bernd Hufmann - Extracted abstract class from LatencyScatterGraphViewer | |
12 | *******************************************************************************/ | |
13 | ||
edded5c1 | 14 | package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.scatter; |
03c96217 | 15 | |
21c917fe | 16 | import java.text.Format; |
03c96217 BH |
17 | import java.util.ArrayList; |
18 | import java.util.Collection; | |
19 | import java.util.Collections; | |
20 | import java.util.Iterator; | |
21 | import java.util.List; | |
e18d40d0 | 22 | import java.util.concurrent.atomic.AtomicInteger; |
03c96217 BH |
23 | |
24 | import org.eclipse.core.runtime.IProgressMonitor; | |
25 | import org.eclipse.core.runtime.IStatus; | |
26 | import org.eclipse.core.runtime.Status; | |
27 | import org.eclipse.core.runtime.jobs.Job; | |
28 | import org.eclipse.jdt.annotation.Nullable; | |
03c96217 BH |
29 | import org.eclipse.swt.widgets.Composite; |
30 | import org.eclipse.swt.widgets.Display; | |
03c96217 | 31 | import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener; |
0f769d2b | 32 | import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider; |
21c917fe | 33 | import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat; |
03c96217 | 34 | import org.eclipse.tracecompass.internal.analysis.timing.ui.Activator; |
edded5c1 | 35 | import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter.Messages; |
21c917fe | 36 | import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter.SegmentStoreScatterGraphTooltipProvider; |
03c96217 BH |
37 | import org.eclipse.tracecompass.segmentstore.core.ISegment; |
38 | import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; | |
39 | import org.eclipse.tracecompass.segmentstore.core.SegmentComparators; | |
0f769d2b | 40 | import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; |
03c96217 | 41 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; |
03c96217 BH |
42 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal; |
43 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; | |
44 | import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal; | |
03c96217 BH |
45 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; |
46 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
47 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; | |
03c96217 | 48 | import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer; |
03c96217 BH |
49 | import org.swtchart.ILineSeries; |
50 | import org.swtchart.ILineSeries.PlotSymbolType; | |
51 | import org.swtchart.ISeries.SeriesType; | |
52 | import org.swtchart.ISeriesSet; | |
53 | import org.swtchart.LineStyle; | |
85ca1cfe MK |
54 | |
55 | import com.google.common.primitives.Doubles; | |
03c96217 BH |
56 | |
57 | /** | |
0f769d2b | 58 | * Displays the segment store provider data in a scatter graph |
03c96217 BH |
59 | * |
60 | * @author France Lapointe Nguyen | |
61 | * @author Matthew Khouzam - reduced memory usage | |
62 | * @since 2.0 | |
63 | */ | |
64 | public abstract class AbstractSegmentStoreScatterGraphViewer extends TmfCommonXLineChartViewer { | |
03c96217 | 65 | |
21c917fe MK |
66 | private static final Format FORMAT = new SubSecondTimeWithUnitFormat(); |
67 | ||
e18d40d0 GB |
68 | private final AtomicInteger fDirty = new AtomicInteger(); |
69 | ||
03c96217 BH |
70 | private final class CompactingSegmentStoreQuery extends Job { |
71 | private static final long MAX_POINTS = 1000; | |
edb7c563 GB |
72 | private final long fStart; |
73 | private final long fEnd; | |
03c96217 | 74 | |
edb7c563 | 75 | private CompactingSegmentStoreQuery(long start, long end) { |
03c96217 | 76 | super(Messages.SegmentStoreScatterGraphViewer_compactTitle); |
edb7c563 GB |
77 | fStart = start; |
78 | fEnd = end; | |
03c96217 BH |
79 | } |
80 | ||
81 | @Override | |
82 | protected IStatus run(@Nullable IProgressMonitor monitor) { | |
83 | final IProgressMonitor statusMonitor = monitor; | |
e18d40d0 GB |
84 | try { |
85 | if (statusMonitor == null) { | |
86 | return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Monitor is null"); //$NON-NLS-1$ | |
87 | } | |
03c96217 | 88 | |
e18d40d0 | 89 | ISegmentStoreProvider segmentProvider = getSegmentProvider(); |
edb7c563 GB |
90 | final long startTime = fStart; |
91 | final long endTime = fEnd; | |
e18d40d0 | 92 | if (segmentProvider == null) { |
edb7c563 | 93 | redraw(statusMonitor, startTime, startTime, Collections.EMPTY_LIST); |
e18d40d0 GB |
94 | return new Status(IStatus.WARNING, Activator.PLUGIN_ID, "segment provider not available"); //$NON-NLS-1$ |
95 | } | |
03c96217 | 96 | |
e18d40d0 GB |
97 | final ISegmentStore<ISegment> segStore = segmentProvider.getSegmentStore(); |
98 | if (segStore == null) { | |
edb7c563 | 99 | redraw(statusMonitor, startTime, startTime, Collections.EMPTY_LIST); |
e18d40d0 GB |
100 | return new Status(IStatus.INFO, Activator.PLUGIN_ID, "Segment provider does not have segments"); //$NON-NLS-1$ |
101 | } | |
03c96217 | 102 | |
e18d40d0 GB |
103 | fPixelStart = startTime; |
104 | fPixelSize = Math.max(1, (endTime - startTime) / MAX_POINTS); | |
105 | final Iterable<ISegment> intersectingElements = segStore.getIntersectingElements(startTime, endTime); | |
e18d40d0 GB |
106 | final List<ISegment> list = convertIterableToList(intersectingElements, statusMonitor); |
107 | final List<ISegment> displayData = (!list.isEmpty()) ? compactList(startTime, list, statusMonitor) : list; | |
03c96217 | 108 | |
e18d40d0 | 109 | redraw(statusMonitor, startTime, endTime, displayData); |
03c96217 | 110 | |
e18d40d0 GB |
111 | if (statusMonitor.isCanceled()) { |
112 | return Status.CANCEL_STATUS; | |
113 | } | |
114 | return Status.OK_STATUS; | |
115 | } finally { | |
116 | /* | |
117 | * fDirty should have been incremented before creating a job, so | |
118 | * we decrement it once the job is done | |
119 | */ | |
120 | fDirty.decrementAndGet(); | |
03c96217 | 121 | } |
03c96217 BH |
122 | |
123 | } | |
124 | ||
125 | private void redraw(final IProgressMonitor statusMonitor, final long startTime, final long endTime, final List<ISegment> displayData) { | |
126 | fDisplayData = displayData; | |
e18d40d0 GB |
127 | /* |
128 | * Increment at every redraw, since the content of the view is not | |
129 | * current | |
130 | */ | |
131 | fDirty.incrementAndGet(); | |
03c96217 BH |
132 | Display.getDefault().asyncExec(new Runnable() { |
133 | ||
134 | @Override | |
135 | public void run() { | |
e18d40d0 GB |
136 | try { |
137 | updateData(startTime, endTime, displayData.size(), statusMonitor); | |
138 | } finally { | |
139 | /* Decrement once the redraw is done */ | |
140 | fDirty.decrementAndGet(); | |
141 | } | |
03c96217 BH |
142 | } |
143 | }); | |
144 | } | |
145 | ||
146 | private List<ISegment> compactList(final long startTime, final List<ISegment> listToCompact, final IProgressMonitor statusMonitor) { | |
147 | List<ISegment> displayData = new ArrayList<>(); | |
148 | ISegment last = listToCompact.get(0); | |
149 | if (last.getStart() >= startTime) { | |
150 | displayData.add(last); | |
151 | } | |
152 | for (ISegment next : listToCompact) { | |
153 | if (next.getStart() < startTime) { | |
154 | continue; | |
155 | } | |
156 | if (statusMonitor.isCanceled()) { | |
df2597e0 | 157 | return Collections.EMPTY_LIST; |
03c96217 BH |
158 | } |
159 | if (!overlaps(last, next)) { | |
160 | displayData.add(next); | |
161 | last = next; | |
162 | } | |
163 | } | |
164 | return displayData; | |
165 | } | |
166 | ||
167 | private List<ISegment> convertIterableToList(final Iterable<ISegment> iterable, final IProgressMonitor statusMonitor) { | |
168 | final List<ISegment> list = new ArrayList<>(); | |
169 | for (ISegment seg : iterable) { | |
170 | if (statusMonitor.isCanceled()) { | |
df2597e0 | 171 | return Collections.EMPTY_LIST; |
03c96217 BH |
172 | } |
173 | list.add(seg); | |
174 | } | |
175 | Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR); | |
176 | return list; | |
177 | } | |
178 | ||
179 | private boolean overlaps(ISegment last, ISegment next) { | |
180 | long timePerPix = fPixelSize; | |
181 | final long start = last.getStart(); | |
182 | final long pixelStart = fPixelStart; | |
183 | final long pixelDuration = start - pixelStart; | |
184 | long startPixBoundL = pixelDuration / timePerPix * timePerPix + pixelStart; | |
185 | long startPixBoundR = startPixBoundL + timePerPix; | |
186 | final long currentStart = next.getStart(); | |
187 | if (currentStart >= startPixBoundL && currentStart <= startPixBoundR) { | |
188 | long length = last.getLength(); | |
189 | long lengthNext = next.getLength(); | |
190 | long lengthLow = length / timePerPix * timePerPix; | |
191 | long lengthHigh = lengthLow + timePerPix; | |
192 | return (lengthNext >= lengthLow && lengthNext <= lengthHigh); | |
193 | } | |
194 | return false; | |
195 | } | |
196 | } | |
197 | ||
198 | // ------------------------------------------------------------------------ | |
199 | // Attributes | |
200 | // ------------------------------------------------------------------------ | |
201 | ||
202 | /** | |
0f769d2b MK |
203 | * Listener to update the model with the segment store provider results once |
204 | * its segment store is fully completed | |
03c96217 | 205 | */ |
0f769d2b | 206 | private final class SegmentStoreProviderProgressListener implements IAnalysisProgressListener { |
03c96217 BH |
207 | |
208 | @Override | |
0f769d2b | 209 | public void onComplete(ISegmentStoreProvider segmentProvider, ISegmentStore<ISegment> segmentStore) { |
03c96217 | 210 | // Only update the model if trace that was analyzed is active trace |
0f769d2b MK |
211 | if (segmentProvider.equals(getSegmentProvider())) { |
212 | updateModel(segmentStore); | |
03c96217 BH |
213 | } |
214 | } | |
215 | } | |
216 | ||
217 | private long fPixelSize = -1; | |
218 | ||
219 | private long fPixelStart = 0; | |
220 | /** | |
221 | * Data to display | |
222 | */ | |
df2597e0 | 223 | private Collection<ISegment> fDisplayData = Collections.EMPTY_LIST; |
03c96217 BH |
224 | |
225 | /** | |
0f769d2b | 226 | * Provider completion listener |
03c96217 | 227 | */ |
0f769d2b | 228 | private SegmentStoreProviderProgressListener fListener; |
03c96217 BH |
229 | |
230 | /** | |
0f769d2b | 231 | * Current segment provider |
03c96217 | 232 | */ |
0f769d2b | 233 | private @Nullable ISegmentStoreProvider fSegmentProvider; |
03c96217 BH |
234 | |
235 | private @Nullable Job fCompactingJob; | |
236 | ||
237 | // ------------------------------------------------------------------------ | |
238 | // Constructor | |
239 | // ------------------------------------------------------------------------ | |
240 | ||
241 | /** | |
242 | * Constructor | |
243 | * | |
244 | * @param parent | |
245 | * parent composite | |
246 | * @param title | |
247 | * name of the graph | |
248 | * @param xLabel | |
249 | * name of the x axis | |
250 | * @param yLabel | |
251 | * name of the y axis | |
252 | */ | |
253 | public AbstractSegmentStoreScatterGraphViewer(Composite parent, String title, String xLabel, String yLabel) { | |
254 | super(parent, title, xLabel, yLabel); | |
a2de198a | 255 | setTooltipProvider(new SegmentStoreScatterGraphTooltipProvider(this)); |
0f769d2b | 256 | fListener = new SegmentStoreProviderProgressListener(); |
03c96217 | 257 | ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace(); |
0f769d2b | 258 | initializeProvider(trace); |
03c96217 | 259 | getSwtChart().getLegend().setVisible(false); |
21c917fe | 260 | getSwtChart().getAxisSet().getYAxis(0).getTick().setFormat(FORMAT); |
03c96217 BH |
261 | } |
262 | ||
0f769d2b | 263 | private final void initializeProvider(@Nullable ITmfTrace trace) { |
03c96217 | 264 | if (trace != null) { |
0f769d2b MK |
265 | final ISegmentStoreProvider segmentStoreProvider = getSegmentStoreProvider(trace); |
266 | if (segmentStoreProvider != null) { | |
267 | segmentStoreProvider.addListener(fListener); | |
268 | setData(segmentStoreProvider); | |
03c96217 BH |
269 | } |
270 | } | |
271 | } | |
272 | ||
273 | // ------------------------------------------------------------------------ | |
274 | // Operations | |
275 | // ------------------------------------------------------------------------ | |
276 | ||
277 | /** | |
278 | * Update the data in the graph | |
279 | * | |
280 | * @param dataInput | |
281 | * new model | |
282 | */ | |
283 | public void updateModel(@Nullable ISegmentStore<ISegment> dataInput) { | |
284 | // Update new window range | |
285 | TmfTimeRange currentRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange(); | |
16801c72 MK |
286 | long currentStart = currentRange.getStartTime().toNanos(); |
287 | long currentEnd = currentRange.getEndTime().toNanos(); | |
03c96217 BH |
288 | if (dataInput == null) { |
289 | if (!getDisplay().isDisposed()) { | |
290 | Display.getDefault().syncExec(new Runnable() { | |
291 | @Override | |
292 | public void run() { | |
293 | clearContent(); | |
294 | } | |
295 | }); | |
296 | } | |
df2597e0 | 297 | fDisplayData = Collections.EMPTY_LIST; |
03c96217 BH |
298 | } else { |
299 | Collection<ISegment> elements = (Collection<ISegment>) dataInput.getIntersectingElements(currentStart, currentEnd); | |
300 | // getIntersectingElements can return an unsorted iterable, make | |
301 | // sure our collection is sorted | |
302 | ArrayList<ISegment> list = new ArrayList<>(elements); | |
303 | Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR); | |
304 | fDisplayData = list; | |
305 | } | |
306 | setWindowRange(currentStart, currentEnd); | |
edb7c563 | 307 | updateContent(); |
03c96217 BH |
308 | } |
309 | ||
310 | @Override | |
311 | protected void initializeDataSource() { | |
312 | ITmfTrace trace = getTrace(); | |
0f769d2b | 313 | initializeProvider(trace); |
03c96217 | 314 | if (trace != null) { |
0f769d2b | 315 | setData(getSegmentStoreProvider(trace)); |
03c96217 BH |
316 | } |
317 | } | |
318 | ||
319 | @Override | |
320 | protected void updateData(final long start, final long end, int nb, @Nullable IProgressMonitor monitor) { | |
321 | // Third parameter is not used by implementation | |
322 | // Determine data that needs to be visible | |
323 | Collection<ISegment> data = fDisplayData; | |
324 | ||
325 | final int dataSize = (nb == 0) ? data.size() : nb; | |
44af9da9 | 326 | if (end == start) { |
03c96217 BH |
327 | return; |
328 | } | |
329 | ||
85ca1cfe MK |
330 | List<Double> xSeries = new ArrayList<>(dataSize); |
331 | List<Double> ySeries = new ArrayList<>(dataSize); | |
03c96217 BH |
332 | // For each visible segments, add start time to x value and duration |
333 | // for y value | |
334 | Iterator<ISegment> modelIter = data.iterator(); | |
85ca1cfe MK |
335 | while (modelIter.hasNext()) { |
336 | ISegment segment = modelIter.next(); | |
337 | xSeries.add((double) (segment.getStart() - start)); | |
338 | ySeries.add((double) segment.getLength()); | |
03c96217 | 339 | } |
85ca1cfe | 340 | setXAxis(Doubles.toArray(xSeries)); |
44af9da9 | 341 | setSeries(Messages.SegmentStoreScatterGraphViewer_legend, Doubles.toArray(ySeries)); |
85ca1cfe | 342 | updateDisplay(); |
03c96217 BH |
343 | } |
344 | ||
345 | @Override | |
346 | protected void setWindowRange(final long windowStartTime, final long windowEndTime) { | |
347 | super.setWindowRange(windowStartTime, windowEndTime); | |
348 | } | |
349 | ||
350 | @Override | |
351 | protected ILineSeries addSeries(@Nullable String seriesName) { | |
352 | ISeriesSet seriesSet = getSwtChart().getSeriesSet(); | |
353 | ILineSeries series = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName); | |
354 | series.setVisible(true); | |
355 | series.enableArea(false); | |
356 | series.setLineStyle(LineStyle.NONE); | |
357 | series.setSymbolType(PlotSymbolType.DIAMOND); | |
358 | return series; | |
359 | } | |
360 | ||
361 | /** | |
0f769d2b MK |
362 | * Set the data into the viewer. If the provider is an analysis, it will |
363 | * update the model if the analysis is completed or run the analysis if not | |
364 | * completed | |
03c96217 | 365 | * |
0f769d2b MK |
366 | * @param provider |
367 | * Segment store provider | |
03c96217 | 368 | */ |
0f769d2b MK |
369 | public void setData(@Nullable ISegmentStoreProvider provider) { |
370 | if (provider == null) { | |
03c96217 BH |
371 | updateModel(null); |
372 | return; | |
373 | } | |
0f769d2b MK |
374 | ISegmentStore<ISegment> segStore = provider.getSegmentStore(); |
375 | // If results are not null, then segment store is completed and model | |
376 | // can be updated | |
73c74de7 JCK |
377 | if (segStore != null) { |
378 | updateModel(segStore); | |
0f769d2b | 379 | setSegmentProvider(provider); |
03c96217 BH |
380 | return; |
381 | } | |
382 | updateModel(null); | |
0f769d2b MK |
383 | provider.addListener(fListener); |
384 | if (provider instanceof IAnalysisModule) { | |
385 | ((IAnalysisModule) provider).schedule(); | |
386 | } | |
387 | setSegmentProvider(provider); | |
03c96217 BH |
388 | } |
389 | ||
390 | /** | |
0f769d2b | 391 | * Returns the segment store provider |
df2597e0 | 392 | * |
03c96217 BH |
393 | * @param trace |
394 | * The trace to consider | |
0f769d2b | 395 | * @return the segment store provider |
03c96217 | 396 | */ |
0f769d2b | 397 | protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace); |
03c96217 | 398 | |
03c96217 BH |
399 | // ------------------------------------------------------------------------ |
400 | // Signal handlers | |
401 | // ------------------------------------------------------------------------ | |
402 | ||
403 | /** | |
404 | * @param signal | |
405 | * Signal received when a different trace is selected | |
406 | */ | |
407 | @Override | |
408 | @TmfSignalHandler | |
409 | public void traceSelected(@Nullable TmfTraceSelectedSignal signal) { | |
410 | super.traceSelected(signal); | |
411 | if (signal == null) { | |
412 | return; | |
413 | } | |
414 | ITmfTrace trace = signal.getTrace(); | |
415 | setTrace(trace); | |
416 | if (trace != null) { | |
417 | final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange(); | |
418 | setWindowRange( | |
16801c72 MK |
419 | timeRange.getStartTime().toNanos(), |
420 | timeRange.getEndTime().toNanos()); | |
0f769d2b | 421 | setData(getSegmentStoreProvider(trace)); |
edb7c563 | 422 | updateContent(); |
03c96217 BH |
423 | } |
424 | } | |
425 | ||
426 | /** | |
427 | * @param signal | |
428 | * Signal received when trace is opened | |
429 | */ | |
430 | @Override | |
431 | @TmfSignalHandler | |
432 | public void traceOpened(@Nullable TmfTraceOpenedSignal signal) { | |
433 | super.traceOpened(signal); | |
434 | if (signal == null) { | |
435 | return; | |
436 | } | |
437 | ITmfTrace trace = signal.getTrace(); | |
438 | setTrace(trace); | |
439 | if (trace != null) { | |
440 | ||
0f769d2b | 441 | final ISegmentStoreProvider segmentStoreProvider = getSegmentStoreProvider(trace); |
03c96217 BH |
442 | final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange(); |
443 | setWindowRange( | |
16801c72 MK |
444 | timeRange.getStartTime().toNanos(), |
445 | timeRange.getEndTime().toNanos()); | |
0f769d2b | 446 | setData(segmentStoreProvider); |
03c96217 BH |
447 | } |
448 | ||
449 | } | |
450 | ||
edb7c563 GB |
451 | @Override |
452 | protected void updateContent() { | |
e18d40d0 | 453 | /* |
edb7c563 | 454 | * Update is requested, content is not up to date, fDirty will be |
e18d40d0 GB |
455 | * decremented in the compacting job |
456 | */ | |
457 | fDirty.incrementAndGet(); | |
03c96217 BH |
458 | Job compactingJob = fCompactingJob; |
459 | if (compactingJob != null && compactingJob.getState() == Job.RUNNING) { | |
460 | compactingJob.cancel(); | |
461 | } | |
edb7c563 | 462 | compactingJob = new CompactingSegmentStoreQuery(getWindowStartTime(), getWindowEndTime()); |
03c96217 BH |
463 | fCompactingJob = compactingJob; |
464 | compactingJob.schedule(); | |
465 | } | |
466 | ||
467 | /** | |
468 | * @param signal | |
469 | * Signal received when last opened trace is closed | |
470 | */ | |
471 | @Override | |
472 | @TmfSignalHandler | |
473 | public void traceClosed(@Nullable TmfTraceClosedSignal signal) { | |
474 | super.traceClosed(signal); | |
475 | if (signal != null) { | |
476 | // Check if there is no more opened trace | |
477 | if (TmfTraceManager.getInstance().getActiveTrace() == null) { | |
0f769d2b MK |
478 | ISegmentStoreProvider provider = getSegmentProvider(); |
479 | if (provider != null) { | |
480 | provider.removeListener(fListener); | |
03c96217 BH |
481 | } |
482 | clearContent(); | |
483 | } | |
484 | } | |
485 | refresh(); | |
486 | } | |
487 | ||
0f769d2b MK |
488 | private @Nullable ISegmentStoreProvider getSegmentProvider() { |
489 | return fSegmentProvider; | |
03c96217 BH |
490 | } |
491 | ||
0f769d2b MK |
492 | private void setSegmentProvider(ISegmentStoreProvider provider) { |
493 | fSegmentProvider = provider; | |
03c96217 | 494 | } |
e18d40d0 GB |
495 | |
496 | @Override | |
497 | public boolean isDirty() { | |
498 | /* Check the parent's or this view's own dirtiness */ | |
499 | return super.isDirty() || (fDirty.get() != 0); | |
500 | } | |
03c96217 | 501 | } |