tmf: Add support for blending sub-pixel events in time graph
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / rawviewer / TmfRawEventViewer.java
CommitLineData
7d048a33 1/*******************************************************************************
812e7197 2 * Copyright (c) 2010, 2015 Ericsson
7d048a33
PT
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 * Patrick Tasse - Initial API and implementation
11 ******************************************************************************/
12
2bdf0193 13package org.eclipse.tracecompass.tmf.ui.widgets.rawviewer;
7d048a33
PT
14
15import java.util.ArrayList;
16import java.util.List;
17
332c750a 18import org.eclipse.jface.resource.ColorRegistry;
812e7197
PT
19import org.eclipse.jface.resource.FontRegistry;
20import org.eclipse.jface.util.IPropertyChangeListener;
21import org.eclipse.jface.util.PropertyChangeEvent;
7d048a33
PT
22import org.eclipse.swt.SWT;
23import org.eclipse.swt.custom.CaretEvent;
24import org.eclipse.swt.custom.CaretListener;
25import org.eclipse.swt.custom.ScrolledComposite;
26import org.eclipse.swt.custom.StyledText;
27import org.eclipse.swt.events.ControlEvent;
28import org.eclipse.swt.events.ControlListener;
29import org.eclipse.swt.events.KeyEvent;
30import org.eclipse.swt.events.KeyListener;
31import org.eclipse.swt.events.MouseAdapter;
32import org.eclipse.swt.events.MouseEvent;
29ddb729 33import org.eclipse.swt.events.MouseListener;
7d048a33
PT
34import org.eclipse.swt.events.MouseMoveListener;
35import org.eclipse.swt.events.MouseTrackListener;
36import org.eclipse.swt.events.MouseWheelListener;
37import org.eclipse.swt.events.SelectionEvent;
38import org.eclipse.swt.events.SelectionListener;
39import org.eclipse.swt.graphics.Color;
40import org.eclipse.swt.graphics.Font;
7d048a33
PT
41import org.eclipse.swt.graphics.Point;
42import org.eclipse.swt.layout.GridData;
43import org.eclipse.swt.layout.GridLayout;
44import org.eclipse.swt.widgets.Composite;
45import org.eclipse.swt.widgets.Control;
46import org.eclipse.swt.widgets.Display;
47import org.eclipse.swt.widgets.Event;
48import org.eclipse.swt.widgets.Listener;
49import org.eclipse.swt.widgets.Menu;
50import org.eclipse.swt.widgets.Slider;
2bdf0193
AM
51import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
52import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
53import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
54import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
812e7197
PT
55import org.eclipse.ui.PlatformUI;
56import org.eclipse.ui.themes.IThemeManager;
7d048a33
PT
57
58/**
59 * TmfRawEventViewer allows for the display of the raw data for an arbitrarily
60 * large number of TMF events.
61 *
62 * It is essentially a Composite of a StyledText area and a Slider, where the number
63 * of visible lines in the StyledText control is set to fill the viewer display area.
64 * An underlying data model is used to store a cache of event raw text line data.
65 * The slider is ratio-based.
66 *
67 * @version 1.0
68 * @author Patrick Tasse
69 */
29ddb729 70public class TmfRawEventViewer extends Composite implements ControlListener, SelectionListener, MouseListener,
812e7197 71 KeyListener, CaretListener, MouseMoveListener, MouseTrackListener, MouseWheelListener, IPropertyChangeListener {
7d048a33
PT
72
73 private static final Color COLOR_BACKGROUND_ODD = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
74 private static final Color COLOR_BACKGROUND_EVEN = new Color(Display.getDefault(), 242, 242, 242);
812e7197 75 private static final String FONT_DEFINITION_ID = "org.eclipse.tracecompass.tmf.ui.font.eventraw"; //$NON-NLS-1$
332c750a
PT
76 private static final String HIGHLIGHT_COLOR_DEFINITION_ID = "org.eclipse.tracecompass.tmf.ui.color.eventraw.highlight"; //$NON-NLS-1$
77 private static final String SELECTION_COLOR_DEFINITION_ID = "org.eclipse.tracecompass.tmf.ui.color.eventraw.selection"; //$NON-NLS-1$
7d048a33
PT
78 private static final int MAX_LINE_DATA_SIZE = 1000;
79 private static final int SLIDER_MAX = 1000000;
80
81 private ITmfTrace fTrace;
82 private ITmfContext fBottomContext;
83
84 private ScrolledComposite fScrolledComposite;
85 private Composite fTextArea;
86 private StyledText fStyledText;
87 private Font fFixedFont;
332c750a
PT
88 private Color fHighlightColor;
89 private Color fSelectionColor;
d5efe032 90 private Slider fSlider;
29ddb729 91 private SliderThrottler fSliderThrottler;
d5efe032
AF
92
93 private final List<LineData> fLines = new ArrayList<>();
94 private boolean fActualRanks = false;
95 private int fTopLineIndex;
96 private int fLastTopLineIndex;
97 private final CaretPosition[] fStoredCaretPosition = new CaretPosition[]
98 { new CaretPosition(0, 0), new CaretPosition(0,0)};
99 private int fNumVisibleLines;
7d048a33
PT
100 private ITmfLocation fSelectedLocation = null;
101 private long fHighlightedRank = Long.MIN_VALUE;
102 private int fCursorYCoordinate = -1;
103 private int fHoldSelection = 0;
104
29ddb729
PT
105 // ------------------------------------------------------------------------
106 // Classes
107 // ------------------------------------------------------------------------
108
d5efe032
AF
109 private static class LineData {
110 long rank;
111 ITmfLocation location;
112 String string;
113 public LineData(long rank, ITmfLocation location, String string) {
114 this.rank = rank;
115 this.location = location;
7d048a33
PT
116 if (string.length() == 0) {
117 this.string = " "; // workaround for setLineBackground has no effect on empty line //$NON-NLS-1$
118 } else {
119 this.string = string;
120 }
d5efe032 121 }
7d048a33
PT
122 @Override
123 public String toString() {
124 return rank + " [" + location + "]: " + string; //$NON-NLS-1$ //$NON-NLS-2$
125 }
d5efe032
AF
126 }
127
128 private static class CaretPosition {
129 int time;
130 int caretOffset;
131 public CaretPosition(int time, int caretOffset) {
132 this.time = time;
133 this.caretOffset = caretOffset;
134 }
135 }
136
29ddb729
PT
137 private class SliderThrottler extends Thread {
138 private static final long DELAY = 400L;
139 private static final long POLLING_INTERVAL = 10L;
140
141 @Override
142 public void run() {
143 final long startTime = System.currentTimeMillis();
144 while ((System.currentTimeMillis() - startTime) < DELAY) {
145 try {
146 Thread.sleep(POLLING_INTERVAL);
147 } catch (InterruptedException e) {
148 }
149 }
150 Display.getDefault().asyncExec(new Runnable() {
151 @Override
152 public void run() {
153 if (fSliderThrottler != SliderThrottler.this) {
154 return;
155 }
156 fSliderThrottler = null;
157 if (SliderThrottler.this.isInterrupted() || fSlider.isDisposed()) {
158 return;
159 }
160 Event event = new Event();
161 event.widget = TmfRawEventViewer.this;
162 event.detail = SWT.NONE;
163 widgetSelected(new SelectionEvent(event));
164 }
165 });
166 }
167 }
168
d5efe032
AF
169 // ------------------------------------------------------------------------
170 // Constructor
171 // ------------------------------------------------------------------------
172
173 /**
174 * Constructor
175 * @param parent The parent composite
176 * @param style The style bits
177 */
178 public TmfRawEventViewer(Composite parent, int style) {
179 super(parent, style & (~SWT.H_SCROLL) & (~SWT.V_SCROLL));
180
181 // Set the layout
182 GridLayout gridLayout = new GridLayout();
183 gridLayout.numColumns = 2;
184 gridLayout.horizontalSpacing = 0;
185 gridLayout.verticalSpacing = 0;
7d048a33
PT
186 gridLayout.marginWidth = 0;
187 gridLayout.marginHeight = 0;
d5efe032 188 setLayout(gridLayout);
7d048a33 189
d5efe032
AF
190 // Create the controls
191 createTextArea(style & SWT.H_SCROLL);
192 createSlider(style & SWT.V_SCROLL);
7d048a33 193
d5efe032
AF
194 // Prevent the slider from being traversed
195 setTabList(new Control[] { fScrolledComposite });
196 }
7d048a33
PT
197
198 @Override
d5efe032 199 public void dispose() {
812e7197 200 PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(this);
d5efe032
AF
201 super.dispose();
202 }
7d048a33 203
812e7197 204 // ------------------------------------------------------------------------
332c750a 205 // Font and color handling
812e7197
PT
206 // ------------------------------------------------------------------------
207
208 /**
209 * Initialize the fonts.
210 * @since 1.0
211 */
212 protected void initializeFonts() {
213 FontRegistry fontRegistry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry();
214 fFixedFont = fontRegistry.get(FONT_DEFINITION_ID);
215 fStyledText.setFont(fFixedFont);
216 }
217
332c750a
PT
218 /**
219 * Initialize the colors.
34c87ae8 220 * @since 2.0
332c750a
PT
221 */
222 protected void initializeColors() {
223 ColorRegistry colorRegistry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry();
224 fHighlightColor = colorRegistry.get(HIGHLIGHT_COLOR_DEFINITION_ID);
225 fSelectionColor = colorRegistry.get(SELECTION_COLOR_DEFINITION_ID);
226 }
227
812e7197
PT
228 /**
229 * @since 1.0
230 */
231 @Override
232 public void propertyChange(PropertyChangeEvent event) {
233 if ((IThemeManager.CHANGE_CURRENT_THEME.equals(event.getProperty())) ||
234 (FONT_DEFINITION_ID.equals(event.getProperty()))) {
235 initializeFonts();
236 refreshTextArea();
237 }
332c750a
PT
238 if ((IThemeManager.CHANGE_CURRENT_THEME.equals(event.getProperty())) ||
239 (HIGHLIGHT_COLOR_DEFINITION_ID.equals(event.getProperty())) ||
240 (SELECTION_COLOR_DEFINITION_ID.equals(event.getProperty()))) {
241 initializeColors();
242 refreshTextArea();
243 }
812e7197
PT
244 }
245
d5efe032
AF
246 // ------------------------------------------------------------------------
247 // Text area handling
248 // ------------------------------------------------------------------------
7d048a33
PT
249
250 /**
d5efe032
AF
251 * Create the text area and add listeners
252 */
253 private void createTextArea(int style) {
254 fScrolledComposite = new ScrolledComposite(this, style);
255 fScrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
256 fTextArea = new Composite(fScrolledComposite, SWT.NONE);
7d048a33
PT
257 fTextArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
258 fScrolledComposite.setContent(fTextArea);
259 fScrolledComposite.setExpandHorizontal(true);
260 fScrolledComposite.setExpandVertical(true);
261 fScrolledComposite.setAlwaysShowScrollBars(true);
262 fScrolledComposite.setMinSize(fTextArea.computeSize(SWT.DEFAULT, SWT.DEFAULT));
263 fScrolledComposite.addControlListener(this);
264
265 GridLayout textAreaGridLayout = new GridLayout();
266 textAreaGridLayout.marginHeight = 0;
267 textAreaGridLayout.marginWidth = 0;
268 fTextArea.setLayout(textAreaGridLayout);
269
7d048a33 270 fStyledText = new StyledText(fTextArea, SWT.READ_ONLY);
7d048a33
PT
271 fStyledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
272
812e7197 273 initializeFonts();
332c750a 274 initializeColors();
812e7197
PT
275 PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(this);
276
7d048a33
PT
277 fStyledText.addCaretListener(this);
278 fStyledText.addMouseMoveListener(this);
279 fStyledText.addMouseTrackListener(this);
280 fStyledText.addMouseWheelListener(this);
281 fStyledText.addListener(SWT.MouseWheel, new Listener() { // disable mouse scroll of horizontal scroll bar
282 @Override
283 public void handleEvent(Event event) { event.doit = false; }});
284 fStyledText.addKeyListener(this);
285
286 fTextArea.setBackground(fStyledText.getBackground());
287 fTextArea.addMouseListener(new MouseAdapter() {
d5efe032 288 @Override
7d048a33 289 public void mouseDown(MouseEvent e) {
d5efe032 290 fTextArea.setFocus();
7d048a33 291 }});
d5efe032 292 }
7d048a33 293
d5efe032
AF
294 // ------------------------------------------------------------------------
295 // Slider handling
296 // ------------------------------------------------------------------------
7d048a33 297
d5efe032
AF
298 private void createSlider(int style) {
299 fSlider = new Slider(this, SWT.VERTICAL);
7d048a33
PT
300 fSlider.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
301 fSlider.setValues(0, 0, SLIDER_MAX, SLIDER_MAX, 1, 1);
d5efe032 302 fSlider.addSelectionListener(this);
29ddb729 303 fSlider.addMouseListener(this);
7d048a33
PT
304 if ((style & SWT.V_SCROLL) == 0) {
305 fSlider.setVisible(false);
306 }
d5efe032 307 }
7d048a33 308
d5efe032
AF
309 // ------------------------------------------------------------------------
310 // Controls interactions
311 // ------------------------------------------------------------------------
7d048a33 312
d5efe032
AF
313 @Override
314 public boolean setFocus() {
315 boolean isVisible = isVisible();
316 if (isVisible) {
317 fTextArea.setFocus();
318 }
319 return isVisible;
320 }
7d048a33
PT
321
322 @Override
323 public void setMenu(Menu menu) {
324 fStyledText.setMenu(menu);
325 }
326
327 /**
328 * Sets the trace and updates the content
329 * @param trace The trace to set
330 */
331 public void setTrace(ITmfTrace trace) {
d5efe032
AF
332 fTrace = trace;
333 fTopLineIndex = 0;
334 fLines.clear();
335 refreshEventCount();
336 }
7d048a33
PT
337
338 /**
339 * Refreshes the event count, updates the slider thumb and loads display
340 */
d5efe032
AF
341 public void refreshEventCount() {
342 if (fTrace != null) {
343 if (fTrace.getNbEvents() > 0) {
344 fSlider.setThumb((int) Math.max(SLIDER_MAX / fTrace.getNbEvents(), 1));
345 } else {
346 fSlider.setThumb(SLIDER_MAX);
347 }
348
349 if (!isVisible()) {
7d048a33
PT
350 return;
351 }
352
d5efe032
AF
353 if (fLines.size() == 0) {
354 setTopRank(0);
355 } else if (fLines.size() < fNumVisibleLines) {
356 fBottomContext = null;
357 loadLineData();
358 fillTextArea();
359 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
360 fSlider.setSelection((int) (SLIDER_MAX * fTrace.getLocationRatio(fLines.get(fTopLineIndex).location)));
361 }
7d048a33
PT
362 } else {
363 fBottomContext = null;
364 fillTextArea();
365 fSlider.setThumb(SLIDER_MAX);
366 fSlider.setSelection(0);
d5efe032
AF
367 }
368 }
7d048a33 369
d5efe032
AF
370 /**
371 * Selects the event of given rank and makes it visible.
372 * @param rank The rank of event
373 */
7d048a33
PT
374 public void selectAndReveal(long rank) {
375 if (fTrace == null || !isVisible()) {
376 return;
377 }
378 if (fActualRanks && fTopLineIndex < fLines.size() && rank >= fLines.get(fTopLineIndex).rank) {
379 int lastVisibleIndex = Math.min(fTopLineIndex + fNumVisibleLines, fLines.size()) - 1;
380 if (rank <= fLines.get(lastVisibleIndex).rank) {
381 for (int i = fTopLineIndex; i < fLines.size(); i++) {
382 if (fLines.get(i).rank == rank) {
383 fSelectedLocation = fLines.get(i).location;
384 break;
385 }
386 }
387 refreshLineBackgrounds();
388 return;
389 }
390 }
391 setTopRank(rank);
392 if (fLines.size() > 0 && fHoldSelection == 0) {
393 fSelectedLocation = fLines.get(0).location;
394 refreshLineBackgrounds();
395 }
396 }
397
398 /**
399 * Add a selection listener
400 * @param listener A listener to add
401 */
402 public void addSelectionListener(Listener listener) {
403 checkWidget();
404 if (listener == null) {
405 SWT.error (SWT.ERROR_NULL_ARGUMENT);
406 }
407 addListener (SWT.Selection, listener);
408 }
409
410 /**
411 * Remove selection listener
412 * @param listener A listener to remove
413 */
414 public void removeSelectionListener(Listener listener) {
415 checkWidget();
416 if (listener == null) {
417 SWT.error (SWT.ERROR_NULL_ARGUMENT);
418 }
419 removeListener(SWT.Selection, listener);
420 }
421
422 private void sendSelectionEvent(LineData lineData) {
423 Event event = new Event();
424 if (fActualRanks) {
425 event.data = Long.valueOf(lineData.rank);
426 } else {
427 event.data = lineData.location;
428 }
429 notifyListeners(SWT.Selection, event);
430 }
431
432 private void setTopRank(long rank) {
433 fBottomContext = fTrace.seekEvent(rank);
434 if (fBottomContext == null) {
435 return;
436 }
437 fLines.clear();
438 fActualRanks = true;
439 fTopLineIndex = 0;
440 loadLineData();
441 refreshTextArea();
442 if (fLines.size() == 0) {
443 fSlider.setSelection(0);
444 } else {
445 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
446 fSlider.setSelection((int) (SLIDER_MAX * fTrace.getLocationRatio(fLines.get(fTopLineIndex).location)));
447 }
448 }
449
450 private void setTopPosition(double ratio) {
451 fBottomContext = fTrace.seekEvent(ratio);
452 if (fBottomContext == null) {
453 return;
454 }
455 fBottomContext.setRank(0);
456 fLines.clear();
457 fActualRanks = false;
458 fTopLineIndex = 0;
459 loadLineData();
460 refreshTextArea();
461 }
462
d5efe032 463 private void loadLineData() {
7d048a33
PT
464 if (fTopLineIndex < 0) {
465 //if (fLines.size() > 0 && fLines.get(0).rank > 0) {
466 //long endRank = fLines.get(0).rank;
467 //long startRank = Math.max(0, endRank - fNumVisibleLines);
468 //TmfContext context = fTrace.seekEvent(startRank);
469 //int index = 0;
470 //while (context.getRank() < endRank) {
471 //long rank = context.getRank();
472 //ITmfLocation<?> location = context.getLocation();
473 //TmfEvent event = fTrace.getNextEvent(context);
474 //String[] lines = event.getRawText().split("\r?\n");
475 //for (int i = 0; i < lines.length; i++) {
476 //String line = lines[i];
477 //LineData lineData = new LineData(rank, location, line);
478 //fLines.add(index++, lineData);
479 //fTopLineIndex++;
480 //fLastTopLineIndex++;
481 //}
482 //}
483 //}
484 if (fLines.size() > 0 && fTrace.getLocationRatio(fLines.get(0).location) > 0) {
d5efe032 485 double lastRatio = fTrace.getLocationRatio(fLines.get(fLines.size() - 1).location);
7d048a33
PT
486 double firstRatio = fTrace.getLocationRatio(fLines.get(0).location);
487 double delta;
488 boolean singleEvent = false;
489 if (firstRatio != lastRatio) {
490 // approximate ratio of at least 20 items
491 delta = Math.max(20, fNumVisibleLines) * (lastRatio - firstRatio) / (fLines.size() - 1);
492 } else {
493 delta = Math.pow(10, -15);
494 singleEvent = true;
495 }
496 while (fTopLineIndex < 0) {
497 ITmfLocation endLocation = fLines.get(0).location;
498 firstRatio = Math.max(0, firstRatio - delta);
499 ITmfContext context = fTrace.seekEvent(firstRatio);
500 ITmfLocation location;
501 int index = 0;
502 long rank = 0;
503 while (!context.getLocation().equals(endLocation)) {
504 location = context.getLocation();
505 ITmfEvent event = fTrace.getNext(context);
506 if (event == null) {
d5efe032 507 break;
7d048a33
PT
508 }
509 if (event.getContent() != null && event.getContent().getValue() != null) {
510 String[] lines = event.getContent().getValue().toString().split("\r?\n"); //$NON-NLS-1$
511 for (int i = 0; i < lines.length; i++) {
512 String line = lines[i];
513 LineData lineData = new LineData(rank, location, line);
514 fLines.add(index++, lineData);
515 fTopLineIndex++;
516 fLastTopLineIndex++;
517 }
518 } else {
519 LineData lineData = new LineData(rank, location, ""); //$NON-NLS-1$
520 fLines.add(index++, lineData);
521 fTopLineIndex++;
522 fLastTopLineIndex++;
523 }
524 rank++;
525 }
526 long rankOffset = fLines.get(index).rank - rank;
527 for (int i = 0; i < index; i++) {
528 fLines.get(i).rank += rankOffset;
529 }
530 if (firstRatio == 0) {
531 break;
532 }
533 if (singleEvent) {
d5efe032 534 delta = Math.min(delta * 10, 0.1);
7d048a33
PT
535 }
536 }
537 }
538 if (fTopLineIndex < 0) {
539 fTopLineIndex = 0;
540 }
541 }
542
d5efe032
AF
543 while (fLines.size() - fTopLineIndex < fNumVisibleLines) {
544 if (fBottomContext == null) {
545 if (fLines.size() == 0) {
546 fBottomContext = fTrace.seekEvent(0);
547 } else {
7d048a33 548 //fBottomContext = fTrace.seekEvent(fLines.get(fLines.size() - 1).rank + 1);
d5efe032
AF
549 fBottomContext = fTrace.seekEvent(fLines.get(fLines.size() - 1).location);
550 fTrace.getNext(fBottomContext);
551 }
552 if (fBottomContext == null) {
553 break;
554 }
555 }
7d048a33
PT
556 long rank = fBottomContext.getRank();
557 ITmfLocation location = fBottomContext.getLocation() != null ? fBottomContext.getLocation() : null;
558 ITmfEvent event = fTrace.getNext(fBottomContext);
559 if (event == null) {
560 break;
561 }
562 if (event.getContent() != null && event.getContent().getValue() != null) {
563 for (String line : event.getContent().getValue().toString().split("\r?\n")) { //$NON-NLS-1$
564 int crPos;
565 if ((crPos = line.indexOf('\r')) != -1) {
d5efe032 566 line = line.substring(0, crPos);
7d048a33
PT
567 }
568 LineData lineData = new LineData(rank, location, line);
569 fLines.add(lineData);
570 }
571 } else {
572 LineData lineData = new LineData(rank, location, ""); //$NON-NLS-1$
573 fLines.add(lineData);
574 }
d5efe032
AF
575 }
576 fTopLineIndex = Math.max(0, Math.min(fTopLineIndex, fLines.size() - 1));
577
578 if (fLines.size() > MAX_LINE_DATA_SIZE) {
579 if (fTopLineIndex < MAX_LINE_DATA_SIZE / 2) {
580 long rank = fLines.get(MAX_LINE_DATA_SIZE - 1).rank;
581 for (int i = MAX_LINE_DATA_SIZE; i < fLines.size(); i++) {
582 if (fLines.get(i).rank > rank) {
583 fLines.subList(i, fLines.size()).clear();
584 fBottomContext = null;
585 break;
586 }
587 }
588 } else {
589 long rank = fLines.get(fLines.size() - MAX_LINE_DATA_SIZE).rank;
590 for (int i = fLines.size() - MAX_LINE_DATA_SIZE - 1; i >= 0; i--) {
7d048a33
PT
591 if (fLines.get(i).rank < rank) {
592 fLines.subList(0, i + 1).clear();
593 fTopLineIndex -= (i + 1);
594 fLastTopLineIndex -= (i + 1);
595 break;
596 }
d5efe032
AF
597 }
598 }
599 }
600 }
7d048a33
PT
601
602 private void refreshTextArea() {
603 fStyledText.setText(""); //$NON-NLS-1$
604 for (int i = 0; i < fLines.size() - fTopLineIndex && i < fNumVisibleLines; i++) {
605 if (i > 0)
606 {
607 fStyledText.append("\n"); //$NON-NLS-1$
608 }
609 LineData lineData = fLines.get(fTopLineIndex + i);
610 fStyledText.append(lineData.string);
611 setLineBackground(i, lineData);
612 }
613 fTextArea.layout();
614 fScrolledComposite.setMinSize(fTextArea.computeSize(SWT.DEFAULT, SWT.DEFAULT));
615 fLastTopLineIndex = fTopLineIndex;
616 }
617
618 private void fillTextArea() {
619 int nextLine = fStyledText.getCharCount() == 0 ? 0 : fStyledText.getLineCount();
620 for (int i = nextLine; i < fLines.size() - fTopLineIndex && i < fNumVisibleLines; i++) {
621 if (i > 0)
622 {
623 fStyledText.append("\n"); //$NON-NLS-1$
624 }
625 LineData lineData = fLines.get(fTopLineIndex + i);
626 fStyledText.append(lineData.string);
627 setLineBackground(i, lineData);
628 }
629 int endLine = Math.min(fNumVisibleLines, fLines.size());
630 if (endLine < fStyledText.getLineCount()) {
d5efe032
AF
631 int endOffset = fStyledText.getOffsetAtLine(endLine) - 1;
632 if (endOffset > fStyledText.getCharCount()) {
7d048a33 633 fHoldSelection++;
d5efe032 634 fStyledText.replaceTextRange(endOffset, fStyledText.getCharCount() - endOffset, ""); //$NON-NLS-1$
7d048a33 635 fHoldSelection--;
d5efe032 636 }
7d048a33
PT
637 }
638 fTextArea.layout();
639 fScrolledComposite.setMinSize(fTextArea.computeSize(SWT.DEFAULT, SWT.DEFAULT));
640 }
641
d5efe032
AF
642 private void updateTextArea() {
643 if (fTopLineIndex < fLastTopLineIndex) {
644 StringBuffer insertedText = new StringBuffer();
645 for (int i = fTopLineIndex; i < fLastTopLineIndex; i++) {
646 insertedText.append(fLines.get(i).string + "\n"); //$NON-NLS-1$
647 }
648 fStyledText.replaceTextRange(0, 0, insertedText.toString());
7d048a33
PT
649 for (int i = 0; i < fLastTopLineIndex - fTopLineIndex; i++) {
650 LineData lineData = fLines.get(fTopLineIndex + i);
651 setLineBackground(i, lineData);
652 }
d5efe032
AF
653 fLastTopLineIndex = fTopLineIndex;
654 } else if (fTopLineIndex > fLastTopLineIndex) {
655 int length = 0;
656 for (int i = 0; i < fTopLineIndex - fLastTopLineIndex && i < fNumVisibleLines; i++) {
657 length += fLines.get(i + fLastTopLineIndex).string.length();
658 if (i < fStyledText.getLineCount()) {
7d048a33
PT
659 length += 1;
660 }
d5efe032
AF
661 }
662 fStyledText.replaceTextRange(0, length, ""); //$NON-NLS-1$
7d048a33
PT
663 fLastTopLineIndex = fTopLineIndex;
664 fillTextArea();
d5efe032 665 }
7d048a33
PT
666 int endLine = Math.min(fNumVisibleLines, fLines.size());
667 if (endLine < fStyledText.getLineCount()) {
d5efe032
AF
668 int endOffset = fStyledText.getOffsetAtLine(endLine) - 1;
669 if (endOffset > fStyledText.getCharCount()) {
670 fStyledText.replaceTextRange(endOffset, fStyledText.getCharCount() - endOffset, ""); //$NON-NLS-1$
671 }
7d048a33 672 }
d5efe032
AF
673 fTextArea.layout();
674 fScrolledComposite.setMinSize(fTextArea.computeSize(SWT.DEFAULT, SWT.DEFAULT));
675 }
7d048a33 676
d5efe032 677 private void refreshLineBackgrounds() {
7d048a33
PT
678 for (int i = 0; (i < fStyledText.getLineCount()) && (i < fNumVisibleLines) && (i < fLines.size() - fTopLineIndex); i++) {
679 LineData lineData = fLines.get(fTopLineIndex + i);
680 setLineBackground(i, lineData);
681 }
d5efe032 682 }
7d048a33 683
d5efe032 684 private void setLineBackground(int index, LineData lineData) {
7d048a33 685 if (lineData.location.equals(fSelectedLocation)) {
332c750a 686 fStyledText.setLineBackground(index, 1, fSelectionColor);
7d048a33 687 } else if (lineData.rank == fHighlightedRank) {
332c750a 688 fStyledText.setLineBackground(index, 1, fHighlightColor);
7d048a33
PT
689 } else if (lineData.rank % 2 == 0) {
690 fStyledText.setLineBackground(index, 1, COLOR_BACKGROUND_EVEN);
691 } else {
692 fStyledText.setLineBackground(index, 1, COLOR_BACKGROUND_ODD);
693 }
d5efe032
AF
694 }
695
696 private void storeCaretPosition(int time, int caretOffset) {
697 if (fStoredCaretPosition[0].time == time) {
698 fStoredCaretPosition[0].caretOffset = caretOffset;
699 } else {
700 fStoredCaretPosition[1] = fStoredCaretPosition[0];
701 fStoredCaretPosition[0] = new CaretPosition(time, caretOffset);
702 }
703 }
704
705 private int getPreviousCaretOffset(int time) {
706 if (fStoredCaretPosition[0].time == time) {
707 return fStoredCaretPosition[1].caretOffset;
708 }
7d048a33 709 return fStoredCaretPosition[0].caretOffset;
d5efe032 710 }
7d048a33 711
d5efe032 712 private void updateHighlightedRank() {
7d048a33
PT
713 if (fCursorYCoordinate < 0 || fCursorYCoordinate > fStyledText.getSize().y) {
714 if (fHighlightedRank != Long.MIN_VALUE) {
715 fHighlightedRank = Long.MIN_VALUE;
716 refreshLineBackgrounds();
717 }
718 return;
719 }
720 int offset = fStyledText.getOffsetAtLocation(new Point(0, fCursorYCoordinate));
721 int line = fStyledText.getLineAtOffset(offset);
722 if (line < fLines.size() - fTopLineIndex) {
723 LineData lineData = fLines.get(fTopLineIndex + line);
724 if (fHighlightedRank != lineData.rank) {
725 fHighlightedRank = lineData.rank;
726 refreshLineBackgrounds();
727 }
728 } else {
729 if (fHighlightedRank != Long.MIN_VALUE) {
730 fHighlightedRank = Long.MIN_VALUE;
731 refreshLineBackgrounds();
732 }
733 }
d5efe032 734 }
7d048a33
PT
735
736 // ------------------------------------------------------------------------
737 // ControlListener (ScrolledComposite)
738 // ------------------------------------------------------------------------
739
740 @Override
741 public void controlResized(ControlEvent event) {
742 int areaHeight = fScrolledComposite.getSize().y;
743 if (fScrolledComposite.getHorizontalBar() != null) {
744 areaHeight -= fScrolledComposite.getHorizontalBar().getSize().y;
745 }
746 int lineHeight = fStyledText.getLineHeight();
747 fNumVisibleLines = Math.max((areaHeight + lineHeight - 1) / lineHeight, 1);
748
749 if (fBottomContext != null) {
750 loadLineData();
751 fillTextArea();
752 }
753 }
754
755 @Override
756 public void controlMoved(ControlEvent e) {
757 }
758
759 // ------------------------------------------------------------------------
760 // SelectionListener (Slider)
761 // ------------------------------------------------------------------------
762
763 @Override
764 public void widgetSelected(SelectionEvent e) {
d5efe032 765 fTextArea.setFocus();
7d048a33
PT
766 if (fLines.size() == 0) {
767 return;
768 }
7d048a33
PT
769 fHoldSelection++;
770 switch (e.detail) {
29ddb729 771 case SWT.DRAG:
7d048a33 772 case SWT.NONE: {
29ddb729
PT
773 if (e.widget == fSlider) {
774 /*
775 * While the slider thumb is being dragged, only perform the
776 * refresh periodically. The event detail during the drag is
777 * SWT.DRAG on Windows and SWT.NONE on Linux.
778 */
779 if (fSliderThrottler == null) {
780 fSliderThrottler = new SliderThrottler();
781 fSliderThrottler.start();
782 }
783 fHoldSelection = 0;
784 return;
785 }
786 /*
787 * The selection event was sent by the viewer, refresh now.
788 */
7d048a33
PT
789 //long rank = (long) (fTrace.getNbEvents() * ((double) fSlider.getSelection() / SLIDER_MAX));
790 //setTopRank(rank);
d5efe032 791 if (fSlider.getSelection() == 0 || fSlider.getThumb() == SLIDER_MAX) {
7d048a33
PT
792 fLines.clear();
793 setTopPosition(0.0);
794 break;
d5efe032 795 }
7d048a33
PT
796 double ratio = (double) fSlider.getSelection() / (SLIDER_MAX - fSlider.getThumb());
797 double delta = Math.pow(10, -15);
798 fLines.clear();
799 while (fLines.size() == 0) {
800 setTopPosition(ratio);
801 if (ratio == 0.0) {
802 break;
803 }
804 delta = Math.min(delta * 10, 0.1);
805 ratio = Math.max(ratio - delta, 0.0);
806 }
807 break;
808 }
809 case SWT.ARROW_DOWN: {
810 if (fTopLineIndex >= fLines.size()) {
811 break;
812 }
813 fTopLineIndex++;
814 loadLineData();
815 updateTextArea();
816 break;
817 }
818 case SWT.PAGE_DOWN: {
819 fTopLineIndex += Math.max(fNumVisibleLines - 1, 1);
820 loadLineData();
821 updateTextArea();
822 break;
823 }
824 case SWT.ARROW_UP: {
825 //if (fLines.size() == 0 || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
826 if (fLines.size() == 0) {// || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
827 break;
828 }
829 fTopLineIndex--;
830 loadLineData();
831 updateTextArea();
832 break;
833 }
834 case SWT.PAGE_UP: {
835 fTopLineIndex -= Math.max(fNumVisibleLines - 1, 1);
836 loadLineData();
837 updateTextArea();
838 break;
839 }
840 case SWT.HOME: {
841 //selectAndReveal(0);
842 setTopPosition(0.0);
843 break;
844 }
845 case SWT.END: {
846 //if (fTrace.getNbEvents() > 0) {
847 //selectAndReveal(fTrace.getNbEvents() - 1);
848 //}
849 double ratio = 1.0;
850 double delta = Math.pow(10, -15);
851 fLines.clear();
852 while (fLines.size() == 0) {
853 setTopPosition(ratio);
854 if (ratio == 0.0) {
855 break;
856 }
857 delta = Math.min(delta * 10, 0.1);
858 ratio = Math.max(ratio - delta, 0.0);
859 }
860 break;
861 }
862 default:
863 break;
864 }
865 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
866 if (e.detail != SWT.NONE) {
867 fSlider.setSelection((int) (SLIDER_MAX * fTrace.getLocationRatio(fLines.get(fTopLineIndex).location)));
868 }
869
870 fHoldSelection = 0;
871 }
872
873 @Override
874 public void widgetDefaultSelected(SelectionEvent e) {
875 }
876
877 // ------------------------------------------------------------------------
29ddb729
PT
878 // MouseListener (Slider)
879 // ------------------------------------------------------------------------
880
881 /**
34c87ae8 882 * @since 2.0
29ddb729
PT
883 */
884 @Override
885 public void mouseDown(MouseEvent e) {
886 }
887
888 /**
34c87ae8 889 * @since 2.0
29ddb729
PT
890 */
891 @Override
892 public void mouseUp(MouseEvent e) {
893 if (e.button != 1) {
894 return;
895 }
896 /*
897 * When the mouse button is released, perform the refresh immediately
898 * and interrupt and discard the slider throttler.
899 */
900 if (fSliderThrottler != null) {
901 fSliderThrottler.interrupt();
902 fSliderThrottler = null;
903 }
904 Event event = new Event();
905 event.widget = this;
906 event.detail = SWT.NONE;
907 widgetSelected(new SelectionEvent(event));
908 }
909
910 /**
34c87ae8 911 * @since 2.0
29ddb729
PT
912 */
913 @Override
914 public void mouseDoubleClick(MouseEvent e) {
915 }
916
917 // ------------------------------------------------------------------------
7d048a33
PT
918 // KeyListener (StyledText)
919 // ------------------------------------------------------------------------
920
921 @Override
922 public void keyPressed(KeyEvent e) {
923 if (fLines.size() == 0) {
924 return;
925 }
926 int caretOffset = fStyledText.getCaretOffset();
927 int previousCaretOffset = getPreviousCaretOffset(e.time);
928 int previousLineAtCaretPosition = fStyledText.getLineAtOffset(previousCaretOffset);
929 int previousColumnAtCaretPosition = getPreviousCaretOffset(e.time) - fStyledText.getOffsetAtLine(previousLineAtCaretPosition);
930 switch (e.keyCode) {
931 case SWT.ARROW_DOWN: {
932 if (previousLineAtCaretPosition < (fNumVisibleLines - 2)) {
933 break;
934 }
935 fHoldSelection++;
936 fTopLineIndex++;
937 loadLineData();
938 updateTextArea();
939 fHoldSelection--;
940 LineData lineData = fLines.get(fTopLineIndex + fStyledText.getLineAtOffset(fStyledText.getCaretOffset()));
941 if (!lineData.location.equals(fSelectedLocation)) {
942 fSelectedLocation = lineData.location;
943 refreshLineBackgrounds();
944 sendSelectionEvent(lineData);
945 }
946 break;
947 }
948 case SWT.PAGE_DOWN: {
949 if (previousLineAtCaretPosition >= (fNumVisibleLines - 1)) {
950 fHoldSelection++;
951 if (fLines.get(fTopLineIndex + previousLineAtCaretPosition).rank % 2 == 0) {
952 fStyledText.setLineBackground(previousLineAtCaretPosition, 1, COLOR_BACKGROUND_EVEN);
953 } else {
954 fStyledText.setLineBackground(previousLineAtCaretPosition, 1, COLOR_BACKGROUND_ODD);
955 }
956 fSelectedLocation = null;
957 fTopLineIndex += Math.max(fNumVisibleLines - 1, 1);
958 loadLineData();
959 updateTextArea();
960 fHoldSelection--;
961 }
962 int line = Math.min(fNumVisibleLines - 1, fStyledText.getLineCount() - 1);
963 int offset = fStyledText.getOffsetAtLine(line);
964 fStyledText.setSelection(offset + Math.min(previousColumnAtCaretPosition, fLines.get(fTopLineIndex + line).string.length()));
965 break;
966 }
967 case SWT.ARROW_RIGHT: {
968 if (previousCaretOffset < fStyledText.getCharCount() || previousLineAtCaretPosition < (fNumVisibleLines - 2)) {
969 break;
970 }
971 fHoldSelection++;
972 fTopLineIndex++;
973 loadLineData();
974 updateTextArea();
975 fHoldSelection--;
976 fStyledText.setSelection(fStyledText.getCaretOffset() + 1);
977 break;
978 }
979 case SWT.ARROW_UP: {
980 if (previousLineAtCaretPosition > 0) {
981 break;
982 }
983 if (fLines.size() == 0) {// || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
984 break;
985 }
986 fHoldSelection++;
987 fTopLineIndex--;
988 loadLineData();
989 updateTextArea();
990 fHoldSelection--;
991 LineData lineData = fLines.get(fTopLineIndex);
992 if (!lineData.location.equals(fSelectedLocation)) {
993 fSelectedLocation = lineData.location;
994 refreshLineBackgrounds();
995 sendSelectionEvent(lineData);
996 }
997 fStyledText.setSelection(caretOffset);
998 break;
999 }
1000 case SWT.PAGE_UP: {
1001 if (previousLineAtCaretPosition > 0) {
1002 break;
1003 }
1004 fHoldSelection++;
1005 fTopLineIndex -= Math.max(fNumVisibleLines - 1, 1);
1006 loadLineData();
1007 updateTextArea();
1008 fHoldSelection--;
1009 LineData lineData = fLines.get(fTopLineIndex);
1010 if (!lineData.location.equals(fSelectedLocation)) {
1011 fSelectedLocation = lineData.location;
1012 refreshLineBackgrounds();
1013 sendSelectionEvent(lineData);
1014 }
1015 fStyledText.setSelection(caretOffset);
1016 break;
1017 }
1018 case SWT.ARROW_LEFT: {
1019 if (previousCaretOffset > 0) {
1020 break;
1021 }
1022 if (fLines.size() == 0) {// || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
1023 break;
1024 }
1025 long topRank = fLines.get(fTopLineIndex).rank;
1026 fHoldSelection++;
1027 fTopLineIndex--;
1028 loadLineData();
1029 updateTextArea();
1030 fHoldSelection--;
1031 LineData lineData = fLines.get(fTopLineIndex);
1032 if (!lineData.location.equals(fSelectedLocation)) {
1033 fSelectedLocation = lineData.location;
1034 refreshLineBackgrounds();
1035 sendSelectionEvent(lineData);
1036 }
1037 if (topRank != fLines.get(fTopLineIndex).rank) {
1038 fStyledText.setSelection(fLines.get(fTopLineIndex).string.length());
1039 }
1040 break;
1041 }
1042 case SWT.HOME: {
1043 if ((e.stateMask & SWT.CTRL) == 0) {
1044 break;
1045 }
1046 //selectAndReveal(0);
1047 setTopPosition(0.0);
1048 LineData lineData = fLines.get(fTopLineIndex);
1049 if (!lineData.location.equals(fSelectedLocation)) {
1050 fSelectedLocation = lineData.location;
1051 refreshLineBackgrounds();
1052 sendSelectionEvent(lineData);
1053 }
1054 break;
1055 }
1056 case SWT.END: {
1057 if ((e.stateMask & SWT.CTRL) == 0) {
1058 break;
1059 }
1060 //if (fTrace.getNbEvents() > 0) {
1061 //selectAndReveal(fTrace.getNbEvents() - 1);
1062 //}
1063 double ratio = 1.0;
1064 double delta = Math.pow(10, -15);
1065 fLines.clear();
1066 while (fLines.size() == 0) {
1067 setTopPosition(ratio);
1068 if (ratio == 0.0) {
1069 break;
1070 }
1071 delta = Math.min(delta * 10, 0.1);
1072 ratio = Math.max(ratio - delta, 0.0);
1073 }
1074 LineData lineData = fLines.get(fTopLineIndex);
1075 if (!lineData.location.equals(fSelectedLocation)) {
1076 fSelectedLocation = lineData.location;
1077 refreshLineBackgrounds();
1078 sendSelectionEvent(lineData);
1079 }
1080 break;
1081 }
1082 default:
1083 break;
1084 }
1085 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
1086 updateHighlightedRank();
1087 fSlider.setSelection((int) (SLIDER_MAX * fTrace.getLocationRatio(fLines.get(fTopLineIndex).location)));
1088 }
1089
1090 @Override
1091 public void keyReleased(KeyEvent e) {
1092 }
1093
1094 // ------------------------------------------------------------------------
1095 // CaretListener (StyledText)
1096 // ------------------------------------------------------------------------
1097
1098 @Override
1099 public void caretMoved(CaretEvent event) {
1100 if (fHoldSelection == 0) {
1101 int line = fStyledText.getLineAtOffset(event.caretOffset);
1102 if (fTopLineIndex + line < fLines.size()) {
1103 LineData lineData = fLines.get(fTopLineIndex + line);
1104 if (!lineData.location.equals(fSelectedLocation)) {
1105 fSelectedLocation = lineData.location;
1106 refreshLineBackgrounds();
1107 sendSelectionEvent(lineData);
1108 }
1109 }
1110 }
1111 storeCaretPosition(event.time, event.caretOffset);
1112 if (fHoldSelection == 0) {
1113 Point caret = fStyledText.getLocationAtOffset(fStyledText.getCaretOffset());
1114 Point origin = fScrolledComposite.getOrigin();
1115 if (origin.x > caret.x) {
1116 origin.x = caret.x;
1117 } else if (caret.x - origin.x > fScrolledComposite.getSize().x) {
1118 origin.x = caret.x - fScrolledComposite.getSize().x + 1;
1119 }
1120 fScrolledComposite.setOrigin(origin);
1121 }
1122 }
1123
1124 // ------------------------------------------------------------------------
1125 // MouseMoveListener (StyledText)
1126 // ------------------------------------------------------------------------
1127
1128 @Override
1129 public void mouseMove(MouseEvent e) {
1130 fCursorYCoordinate = e.y;
1131 if (e.y < 0 || e.y > fStyledText.getSize().y) {
1132 if (fHighlightedRank != Long.MIN_VALUE) {
1133 fHighlightedRank = Long.MIN_VALUE;
1134 refreshLineBackgrounds();
1135 }
1136 return;
1137 }
1138 int offset = fStyledText.getOffsetAtLocation(new Point(0, e.y));
1139 int line = fStyledText.getLineAtOffset(offset);
1140 if (line < fLines.size() - fTopLineIndex) {
1141 LineData lineData = fLines.get(fTopLineIndex + line);
1142 if (fHighlightedRank != lineData.rank) {
1143 fHighlightedRank = lineData.rank;
1144 refreshLineBackgrounds();
1145 }
1146 } else {
1147 if (fHighlightedRank != Long.MIN_VALUE) {
1148 fHighlightedRank = Long.MIN_VALUE;
1149 refreshLineBackgrounds();
1150 }
1151 }
1152 }
1153
1154 // ------------------------------------------------------------------------
1155 // MouseTrackListener (StyledText)
1156 // ------------------------------------------------------------------------
1157
1158 @Override
1159 public void mouseExit(MouseEvent e) {
1160 fCursorYCoordinate = -1;
1161 if (fHighlightedRank != Long.MIN_VALUE) {
1162 fHighlightedRank = Long.MIN_VALUE;
1163 refreshLineBackgrounds();
1164 }
1165 }
1166
1167 @Override
1168 public void mouseEnter(MouseEvent e) {
1169 fCursorYCoordinate = e.y;
1170 }
1171
1172 @Override
1173 public void mouseHover(MouseEvent e) {
1174 }
1175
1176 // ------------------------------------------------------------------------
1177 // MouseWheelListener (StyledText)
1178 // ------------------------------------------------------------------------
1179
1180 @Override
1181 public void mouseScrolled(MouseEvent e) {
1182 if (fLines.size() == 0) {
1183 return;
1184 }
1185 fHoldSelection++;
1186 fTopLineIndex -= e.count;
1187 loadLineData();
1188 updateTextArea();
1189 fHoldSelection = 0;
1190 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
1191 updateHighlightedRank();
1192 fSlider.setSelection((int) (SLIDER_MAX * fTrace.getLocationRatio(fLines.get(fTopLineIndex).location)));
1193 }
1194
1195}
This page took 0.145161 seconds and 5 git commands to generate.