1 /*******************************************************************************
2 * Copyright (c) 2010, 2015 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Patrick Tasse - Initial API and implementation
11 ******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.rawviewer
;
15 import java
.util
.ArrayList
;
16 import java
.util
.List
;
18 import org
.eclipse
.jface
.resource
.ColorRegistry
;
19 import org
.eclipse
.jface
.resource
.FontRegistry
;
20 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
21 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
22 import org
.eclipse
.swt
.SWT
;
23 import org
.eclipse
.swt
.custom
.CaretEvent
;
24 import org
.eclipse
.swt
.custom
.CaretListener
;
25 import org
.eclipse
.swt
.custom
.ScrolledComposite
;
26 import org
.eclipse
.swt
.custom
.StyledText
;
27 import org
.eclipse
.swt
.events
.ControlEvent
;
28 import org
.eclipse
.swt
.events
.ControlListener
;
29 import org
.eclipse
.swt
.events
.KeyEvent
;
30 import org
.eclipse
.swt
.events
.KeyListener
;
31 import org
.eclipse
.swt
.events
.MouseAdapter
;
32 import org
.eclipse
.swt
.events
.MouseEvent
;
33 import org
.eclipse
.swt
.events
.MouseListener
;
34 import org
.eclipse
.swt
.events
.MouseMoveListener
;
35 import org
.eclipse
.swt
.events
.MouseTrackListener
;
36 import org
.eclipse
.swt
.events
.MouseWheelListener
;
37 import org
.eclipse
.swt
.events
.SelectionEvent
;
38 import org
.eclipse
.swt
.events
.SelectionListener
;
39 import org
.eclipse
.swt
.graphics
.Color
;
40 import org
.eclipse
.swt
.graphics
.Font
;
41 import org
.eclipse
.swt
.graphics
.Point
;
42 import org
.eclipse
.swt
.layout
.GridData
;
43 import org
.eclipse
.swt
.layout
.GridLayout
;
44 import org
.eclipse
.swt
.widgets
.Composite
;
45 import org
.eclipse
.swt
.widgets
.Control
;
46 import org
.eclipse
.swt
.widgets
.Display
;
47 import org
.eclipse
.swt
.widgets
.Event
;
48 import org
.eclipse
.swt
.widgets
.Listener
;
49 import org
.eclipse
.swt
.widgets
.Menu
;
50 import org
.eclipse
.swt
.widgets
.Slider
;
51 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
52 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfContext
;
53 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
54 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.location
.ITmfLocation
;
55 import org
.eclipse
.ui
.PlatformUI
;
56 import org
.eclipse
.ui
.themes
.IThemeManager
;
59 * TmfRawEventViewer allows for the display of the raw data for an arbitrarily
60 * large number of TMF events.
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.
68 * @author Patrick Tasse
70 public class TmfRawEventViewer
extends Composite
implements ControlListener
, SelectionListener
, MouseListener
,
71 KeyListener
, CaretListener
, MouseMoveListener
, MouseTrackListener
, MouseWheelListener
, IPropertyChangeListener
{
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);
75 private static final String FONT_DEFINITION_ID
= "org.eclipse.tracecompass.tmf.ui.font.eventraw"; //$NON-NLS-1$
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$
78 private static final int MAX_LINE_DATA_SIZE
= 1000;
79 private static final int SLIDER_MAX
= 1000000;
81 private ITmfTrace fTrace
;
82 private ITmfContext fBottomContext
;
84 private ScrolledComposite fScrolledComposite
;
85 private Composite fTextArea
;
86 private StyledText fStyledText
;
87 private Font fFixedFont
;
88 private Color fHighlightColor
;
89 private Color fSelectionColor
;
90 private Slider fSlider
;
91 private SliderThrottler fSliderThrottler
;
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
;
100 private ITmfLocation fSelectedLocation
= null;
101 private long fHighlightedRank
= Long
.MIN_VALUE
;
102 private int fCursorYCoordinate
= -1;
103 private int fHoldSelection
= 0;
105 // ------------------------------------------------------------------------
107 // ------------------------------------------------------------------------
109 private static class LineData
{
111 ITmfLocation location
;
113 public LineData(long rank
, ITmfLocation location
, String string
) {
115 this.location
= location
;
116 if (string
.length() == 0) {
117 this.string
= " "; // workaround for setLineBackground has no effect on empty line //$NON-NLS-1$
119 this.string
= string
;
123 public String
toString() {
124 return rank
+ " [" + location
+ "]: " + string
; //$NON-NLS-1$ //$NON-NLS-2$
128 private static class CaretPosition
{
131 public CaretPosition(int time
, int caretOffset
) {
133 this.caretOffset
= caretOffset
;
137 private class SliderThrottler
extends Thread
{
138 private static final long DELAY
= 400L;
139 private static final long POLLING_INTERVAL
= 10L;
143 final long startTime
= System
.currentTimeMillis();
144 while ((System
.currentTimeMillis() - startTime
) < DELAY
) {
146 Thread
.sleep(POLLING_INTERVAL
);
147 } catch (InterruptedException e
) {
150 Display
.getDefault().asyncExec(new Runnable() {
153 if (fSliderThrottler
!= SliderThrottler
.this) {
156 fSliderThrottler
= null;
157 if (SliderThrottler
.this.isInterrupted() || fSlider
.isDisposed()) {
160 Event event
= new Event();
161 event
.widget
= TmfRawEventViewer
.this;
162 event
.detail
= SWT
.NONE
;
163 widgetSelected(new SelectionEvent(event
));
169 // ------------------------------------------------------------------------
171 // ------------------------------------------------------------------------
175 * @param parent The parent composite
176 * @param style The style bits
178 public TmfRawEventViewer(Composite parent
, int style
) {
179 super(parent
, style
& (~SWT
.H_SCROLL
) & (~SWT
.V_SCROLL
));
182 GridLayout gridLayout
= new GridLayout();
183 gridLayout
.numColumns
= 2;
184 gridLayout
.horizontalSpacing
= 0;
185 gridLayout
.verticalSpacing
= 0;
186 gridLayout
.marginWidth
= 0;
187 gridLayout
.marginHeight
= 0;
188 setLayout(gridLayout
);
190 // Create the controls
191 createTextArea(style
& SWT
.H_SCROLL
);
192 createSlider(style
& SWT
.V_SCROLL
);
194 // Prevent the slider from being traversed
195 setTabList(new Control
[] { fScrolledComposite
});
199 public void dispose() {
200 if (fBottomContext
!= null) {
201 fBottomContext
.dispose();
203 PlatformUI
.getWorkbench().getThemeManager().removePropertyChangeListener(this);
207 // ------------------------------------------------------------------------
208 // Font and color handling
209 // ------------------------------------------------------------------------
212 * Initialize the fonts.
215 protected void initializeFonts() {
216 FontRegistry fontRegistry
= PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry();
217 fFixedFont
= fontRegistry
.get(FONT_DEFINITION_ID
);
218 fStyledText
.setFont(fFixedFont
);
222 * Initialize the colors.
225 protected void initializeColors() {
226 ColorRegistry colorRegistry
= PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry();
227 fHighlightColor
= colorRegistry
.get(HIGHLIGHT_COLOR_DEFINITION_ID
);
228 fSelectionColor
= colorRegistry
.get(SELECTION_COLOR_DEFINITION_ID
);
235 public void propertyChange(PropertyChangeEvent event
) {
236 if ((IThemeManager
.CHANGE_CURRENT_THEME
.equals(event
.getProperty())) ||
237 (FONT_DEFINITION_ID
.equals(event
.getProperty()))) {
241 if ((IThemeManager
.CHANGE_CURRENT_THEME
.equals(event
.getProperty())) ||
242 (HIGHLIGHT_COLOR_DEFINITION_ID
.equals(event
.getProperty())) ||
243 (SELECTION_COLOR_DEFINITION_ID
.equals(event
.getProperty()))) {
249 // ------------------------------------------------------------------------
250 // Text area handling
251 // ------------------------------------------------------------------------
254 * Create the text area and add listeners
256 private void createTextArea(int style
) {
257 fScrolledComposite
= new ScrolledComposite(this, style
);
258 fScrolledComposite
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
259 fTextArea
= new Composite(fScrolledComposite
, SWT
.NONE
);
260 fTextArea
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
261 fScrolledComposite
.setContent(fTextArea
);
262 fScrolledComposite
.setExpandHorizontal(true);
263 fScrolledComposite
.setExpandVertical(true);
264 fScrolledComposite
.setAlwaysShowScrollBars(true);
265 fScrolledComposite
.setMinSize(fTextArea
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
));
266 fScrolledComposite
.addControlListener(this);
268 GridLayout textAreaGridLayout
= new GridLayout();
269 textAreaGridLayout
.marginHeight
= 0;
270 textAreaGridLayout
.marginWidth
= 0;
271 fTextArea
.setLayout(textAreaGridLayout
);
273 fStyledText
= new StyledText(fTextArea
, SWT
.READ_ONLY
);
274 fStyledText
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
278 PlatformUI
.getWorkbench().getThemeManager().addPropertyChangeListener(this);
280 fStyledText
.addCaretListener(this);
281 fStyledText
.addMouseMoveListener(this);
282 fStyledText
.addMouseTrackListener(this);
283 fStyledText
.addMouseWheelListener(this);
284 fStyledText
.addListener(SWT
.MouseWheel
, new Listener() { // disable mouse scroll of horizontal scroll bar
286 public void handleEvent(Event event
) { event
.doit
= false; }});
287 fStyledText
.addKeyListener(this);
289 fTextArea
.setBackground(fStyledText
.getBackground());
290 fTextArea
.addMouseListener(new MouseAdapter() {
292 public void mouseDown(MouseEvent e
) {
293 fTextArea
.setFocus();
297 // ------------------------------------------------------------------------
299 // ------------------------------------------------------------------------
301 private void createSlider(int style
) {
302 fSlider
= new Slider(this, SWT
.VERTICAL
);
303 fSlider
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, false, true));
304 fSlider
.setValues(0, 0, SLIDER_MAX
, SLIDER_MAX
, 1, 1);
305 fSlider
.addSelectionListener(this);
306 fSlider
.addMouseListener(this);
307 if ((style
& SWT
.V_SCROLL
) == 0) {
308 fSlider
.setVisible(false);
312 // ------------------------------------------------------------------------
313 // Controls interactions
314 // ------------------------------------------------------------------------
317 public boolean setFocus() {
318 boolean isVisible
= isVisible();
320 fTextArea
.setFocus();
326 public void setMenu(Menu menu
) {
327 fStyledText
.setMenu(menu
);
331 * Sets the trace and updates the content
332 * @param trace The trace to set
334 public void setTrace(ITmfTrace trace
) {
342 * Refreshes the event count, updates the slider thumb and loads display
344 public void refreshEventCount() {
345 if (fTrace
!= null) {
346 if (fTrace
.getNbEvents() > 0) {
347 fSlider
.setThumb((int) Math
.max(SLIDER_MAX
/ fTrace
.getNbEvents(), 1));
349 fSlider
.setThumb(SLIDER_MAX
);
356 if (fLines
.size() == 0) {
358 } else if (fLines
.size() < fNumVisibleLines
) {
359 if (fBottomContext
!= null) {
360 fBottomContext
.dispose();
361 fBottomContext
= null;
365 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
366 fSlider
.setSelection((int) (SLIDER_MAX
* fTrace
.getLocationRatio(fLines
.get(fTopLineIndex
).location
)));
369 if (fBottomContext
!= null) {
370 fBottomContext
.dispose();
371 fBottomContext
= null;
374 fSlider
.setThumb(SLIDER_MAX
);
375 fSlider
.setSelection(0);
380 * Selects the event of given rank and makes it visible.
381 * @param rank The rank of event
383 public void selectAndReveal(long rank
) {
384 if (fTrace
== null || !isVisible()) {
387 if (fActualRanks
&& fTopLineIndex
< fLines
.size() && rank
>= fLines
.get(fTopLineIndex
).rank
) {
388 int lastVisibleIndex
= Math
.min(fTopLineIndex
+ fNumVisibleLines
, fLines
.size()) - 1;
389 if (rank
<= fLines
.get(lastVisibleIndex
).rank
) {
390 for (int i
= fTopLineIndex
; i
< fLines
.size(); i
++) {
391 if (fLines
.get(i
).rank
== rank
) {
392 fSelectedLocation
= fLines
.get(i
).location
;
396 refreshLineBackgrounds();
401 if (fLines
.size() > 0 && fHoldSelection
== 0) {
402 fSelectedLocation
= fLines
.get(0).location
;
403 refreshLineBackgrounds();
408 * Add a selection listener
409 * @param listener A listener to add
411 public void addSelectionListener(Listener listener
) {
413 if (listener
== null) {
414 SWT
.error (SWT
.ERROR_NULL_ARGUMENT
);
416 addListener (SWT
.Selection
, listener
);
420 * Remove selection listener
421 * @param listener A listener to remove
423 public void removeSelectionListener(Listener listener
) {
425 if (listener
== null) {
426 SWT
.error (SWT
.ERROR_NULL_ARGUMENT
);
428 removeListener(SWT
.Selection
, listener
);
431 private void sendSelectionEvent(LineData lineData
) {
432 Event event
= new Event();
434 event
.data
= Long
.valueOf(lineData
.rank
);
436 event
.data
= lineData
.location
;
438 notifyListeners(SWT
.Selection
, event
);
441 private void setTopRank(long rank
) {
442 if (fBottomContext
!= null) {
443 fBottomContext
.dispose();
445 fBottomContext
= fTrace
.seekEvent(rank
);
446 if (fBottomContext
== null) {
454 if (fLines
.size() == 0) {
455 fSlider
.setSelection(0);
457 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
458 fSlider
.setSelection((int) (SLIDER_MAX
* fTrace
.getLocationRatio(fLines
.get(fTopLineIndex
).location
)));
462 private void setTopPosition(double ratio
) {
463 if (fBottomContext
!= null) {
464 fBottomContext
.dispose();
466 fBottomContext
= fTrace
.seekEvent(ratio
);
467 if (fBottomContext
== null) {
470 fBottomContext
.setRank(0);
472 fActualRanks
= false;
478 private void loadLineData() {
479 if (fTopLineIndex
< 0) {
480 //if (fLines.size() > 0 && fLines.get(0).rank > 0) {
481 //long endRank = fLines.get(0).rank;
482 //long startRank = Math.max(0, endRank - fNumVisibleLines);
483 //TmfContext context = fTrace.seekEvent(startRank);
485 //while (context.getRank() < endRank) {
486 //long rank = context.getRank();
487 //ITmfLocation<?> location = context.getLocation();
488 //TmfEvent event = fTrace.getNextEvent(context);
489 //String[] lines = event.getRawText().split("\r?\n");
490 //for (int i = 0; i < lines.length; i++) {
491 //String line = lines[i];
492 //LineData lineData = new LineData(rank, location, line);
493 //fLines.add(index++, lineData);
495 //fLastTopLineIndex++;
499 if (fLines
.size() > 0 && fTrace
.getLocationRatio(fLines
.get(0).location
) > 0) {
500 double lastRatio
= fTrace
.getLocationRatio(fLines
.get(fLines
.size() - 1).location
);
501 double firstRatio
= fTrace
.getLocationRatio(fLines
.get(0).location
);
503 boolean singleEvent
= false;
504 if (firstRatio
!= lastRatio
) {
505 // approximate ratio of at least 20 items
506 delta
= Math
.max(20, fNumVisibleLines
) * (lastRatio
- firstRatio
) / (fLines
.size() - 1);
508 delta
= Math
.pow(10, -15);
511 while (fTopLineIndex
< 0) {
512 ITmfLocation endLocation
= fLines
.get(0).location
;
513 firstRatio
= Math
.max(0, firstRatio
- delta
);
514 ITmfContext context
= fTrace
.seekEvent(firstRatio
);
515 ITmfLocation location
;
518 while (!context
.getLocation().equals(endLocation
)) {
519 location
= context
.getLocation();
520 ITmfEvent event
= fTrace
.getNext(context
);
524 if (event
.getContent() != null && event
.getContent().getValue() != null) {
525 String
[] lines
= event
.getContent().getValue().toString().split("\r?\n"); //$NON-NLS-1$
526 for (int i
= 0; i
< lines
.length
; i
++) {
527 String line
= lines
[i
];
528 LineData lineData
= new LineData(rank
, location
, line
);
529 fLines
.add(index
++, lineData
);
534 LineData lineData
= new LineData(rank
, location
, ""); //$NON-NLS-1$
535 fLines
.add(index
++, lineData
);
542 long rankOffset
= fLines
.get(index
).rank
- rank
;
543 for (int i
= 0; i
< index
; i
++) {
544 fLines
.get(i
).rank
+= rankOffset
;
546 if (firstRatio
== 0) {
550 delta
= Math
.min(delta
* 10, 0.1);
554 if (fTopLineIndex
< 0) {
559 while (fLines
.size() - fTopLineIndex
< fNumVisibleLines
) {
560 if (fBottomContext
== null) {
561 if (fLines
.size() == 0) {
562 fBottomContext
= fTrace
.seekEvent(0);
564 //fBottomContext = fTrace.seekEvent(fLines.get(fLines.size() - 1).rank + 1);
565 fBottomContext
= fTrace
.seekEvent(fLines
.get(fLines
.size() - 1).location
);
566 fTrace
.getNext(fBottomContext
);
568 if (fBottomContext
== null) {
572 long rank
= fBottomContext
.getRank();
573 ITmfLocation location
= fBottomContext
.getLocation() != null ? fBottomContext
.getLocation() : null;
574 ITmfEvent event
= fTrace
.getNext(fBottomContext
);
578 if (event
.getContent() != null && event
.getContent().getValue() != null) {
579 for (String line
: event
.getContent().getValue().toString().split("\r?\n")) { //$NON-NLS-1$
581 if ((crPos
= line
.indexOf('\r')) != -1) {
582 line
= line
.substring(0, crPos
);
584 LineData lineData
= new LineData(rank
, location
, line
);
585 fLines
.add(lineData
);
588 LineData lineData
= new LineData(rank
, location
, ""); //$NON-NLS-1$
589 fLines
.add(lineData
);
592 fTopLineIndex
= Math
.max(0, Math
.min(fTopLineIndex
, fLines
.size() - 1));
594 if (fLines
.size() > MAX_LINE_DATA_SIZE
) {
595 if (fTopLineIndex
< MAX_LINE_DATA_SIZE
/ 2) {
596 long rank
= fLines
.get(MAX_LINE_DATA_SIZE
- 1).rank
;
597 for (int i
= MAX_LINE_DATA_SIZE
; i
< fLines
.size(); i
++) {
598 if (fLines
.get(i
).rank
> rank
) {
599 fLines
.subList(i
, fLines
.size()).clear();
600 if (fBottomContext
!= null) {
601 fBottomContext
.dispose();
602 fBottomContext
= null;
608 long rank
= fLines
.get(fLines
.size() - MAX_LINE_DATA_SIZE
).rank
;
609 for (int i
= fLines
.size() - MAX_LINE_DATA_SIZE
- 1; i
>= 0; i
--) {
610 if (fLines
.get(i
).rank
< rank
) {
611 fLines
.subList(0, i
+ 1).clear();
612 fTopLineIndex
-= (i
+ 1);
613 fLastTopLineIndex
-= (i
+ 1);
621 private void refreshTextArea() {
622 fStyledText
.setText(""); //$NON-NLS-1$
623 for (int i
= 0; i
< fLines
.size() - fTopLineIndex
&& i
< fNumVisibleLines
; i
++) {
626 fStyledText
.append("\n"); //$NON-NLS-1$
628 LineData lineData
= fLines
.get(fTopLineIndex
+ i
);
629 fStyledText
.append(lineData
.string
);
630 setLineBackground(i
, lineData
);
633 fScrolledComposite
.setMinSize(fTextArea
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
));
634 fLastTopLineIndex
= fTopLineIndex
;
637 private void fillTextArea() {
638 int nextLine
= fStyledText
.getCharCount() == 0 ?
0 : fStyledText
.getLineCount();
639 for (int i
= nextLine
; i
< fLines
.size() - fTopLineIndex
&& i
< fNumVisibleLines
; i
++) {
642 fStyledText
.append("\n"); //$NON-NLS-1$
644 LineData lineData
= fLines
.get(fTopLineIndex
+ i
);
645 fStyledText
.append(lineData
.string
);
646 setLineBackground(i
, lineData
);
648 int endLine
= Math
.min(fNumVisibleLines
, fLines
.size());
649 if (endLine
< fStyledText
.getLineCount()) {
650 int endOffset
= fStyledText
.getOffsetAtLine(endLine
) - 1;
651 if (endOffset
> fStyledText
.getCharCount()) {
653 fStyledText
.replaceTextRange(endOffset
, fStyledText
.getCharCount() - endOffset
, ""); //$NON-NLS-1$
658 fScrolledComposite
.setMinSize(fTextArea
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
));
661 private void updateTextArea() {
662 if (fTopLineIndex
< fLastTopLineIndex
) {
663 StringBuffer insertedText
= new StringBuffer();
664 for (int i
= fTopLineIndex
; i
< fLastTopLineIndex
; i
++) {
665 insertedText
.append(fLines
.get(i
).string
+ "\n"); //$NON-NLS-1$
667 fStyledText
.replaceTextRange(0, 0, insertedText
.toString());
668 for (int i
= 0; i
< fLastTopLineIndex
- fTopLineIndex
; i
++) {
669 LineData lineData
= fLines
.get(fTopLineIndex
+ i
);
670 setLineBackground(i
, lineData
);
672 fLastTopLineIndex
= fTopLineIndex
;
673 } else if (fTopLineIndex
> fLastTopLineIndex
) {
675 for (int i
= 0; i
< fTopLineIndex
- fLastTopLineIndex
&& i
< fNumVisibleLines
; i
++) {
676 length
+= fLines
.get(i
+ fLastTopLineIndex
).string
.length();
677 if (i
< fStyledText
.getLineCount()) {
681 fStyledText
.replaceTextRange(0, length
, ""); //$NON-NLS-1$
682 fLastTopLineIndex
= fTopLineIndex
;
685 int endLine
= Math
.min(fNumVisibleLines
, fLines
.size());
686 if (endLine
< fStyledText
.getLineCount()) {
687 int endOffset
= fStyledText
.getOffsetAtLine(endLine
) - 1;
688 if (endOffset
> fStyledText
.getCharCount()) {
689 fStyledText
.replaceTextRange(endOffset
, fStyledText
.getCharCount() - endOffset
, ""); //$NON-NLS-1$
693 fScrolledComposite
.setMinSize(fTextArea
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
));
696 private void refreshLineBackgrounds() {
697 for (int i
= 0; (i
< fStyledText
.getLineCount()) && (i
< fNumVisibleLines
) && (i
< fLines
.size() - fTopLineIndex
); i
++) {
698 LineData lineData
= fLines
.get(fTopLineIndex
+ i
);
699 setLineBackground(i
, lineData
);
703 private void setLineBackground(int index
, LineData lineData
) {
704 if (lineData
.location
.equals(fSelectedLocation
)) {
705 fStyledText
.setLineBackground(index
, 1, fSelectionColor
);
706 } else if (lineData
.rank
== fHighlightedRank
) {
707 fStyledText
.setLineBackground(index
, 1, fHighlightColor
);
708 } else if (lineData
.rank
% 2 == 0) {
709 fStyledText
.setLineBackground(index
, 1, COLOR_BACKGROUND_EVEN
);
711 fStyledText
.setLineBackground(index
, 1, COLOR_BACKGROUND_ODD
);
715 private void storeCaretPosition(int time
, int caretOffset
) {
716 if (fStoredCaretPosition
[0].time
== time
) {
717 fStoredCaretPosition
[0].caretOffset
= caretOffset
;
719 fStoredCaretPosition
[1] = fStoredCaretPosition
[0];
720 fStoredCaretPosition
[0] = new CaretPosition(time
, caretOffset
);
724 private int getPreviousCaretOffset(int time
) {
725 if (fStoredCaretPosition
[0].time
== time
) {
726 return fStoredCaretPosition
[1].caretOffset
;
728 return fStoredCaretPosition
[0].caretOffset
;
731 private void updateHighlightedRank() {
732 if (fCursorYCoordinate
< 0 || fCursorYCoordinate
> fStyledText
.getSize().y
) {
733 if (fHighlightedRank
!= Long
.MIN_VALUE
) {
734 fHighlightedRank
= Long
.MIN_VALUE
;
735 refreshLineBackgrounds();
739 int offset
= fStyledText
.getOffsetAtLocation(new Point(0, fCursorYCoordinate
));
740 int line
= fStyledText
.getLineAtOffset(offset
);
741 if (line
< fLines
.size() - fTopLineIndex
) {
742 LineData lineData
= fLines
.get(fTopLineIndex
+ line
);
743 if (fHighlightedRank
!= lineData
.rank
) {
744 fHighlightedRank
= lineData
.rank
;
745 refreshLineBackgrounds();
748 if (fHighlightedRank
!= Long
.MIN_VALUE
) {
749 fHighlightedRank
= Long
.MIN_VALUE
;
750 refreshLineBackgrounds();
755 // ------------------------------------------------------------------------
756 // ControlListener (ScrolledComposite)
757 // ------------------------------------------------------------------------
760 public void controlResized(ControlEvent event
) {
761 int areaHeight
= fScrolledComposite
.getSize().y
;
762 if (fScrolledComposite
.getHorizontalBar() != null) {
763 areaHeight
-= fScrolledComposite
.getHorizontalBar().getSize().y
;
765 int lineHeight
= fStyledText
.getLineHeight();
766 fNumVisibleLines
= Math
.max((areaHeight
+ lineHeight
- 1) / lineHeight
, 1);
768 if (fBottomContext
!= null) {
775 public void controlMoved(ControlEvent e
) {
778 // ------------------------------------------------------------------------
779 // SelectionListener (Slider)
780 // ------------------------------------------------------------------------
783 public void widgetSelected(SelectionEvent e
) {
784 fTextArea
.setFocus();
785 if (fLines
.size() == 0) {
792 if (e
.widget
== fSlider
) {
794 * While the slider thumb is being dragged, only perform the
795 * refresh periodically. The event detail during the drag is
796 * SWT.DRAG on Windows and SWT.NONE on Linux.
798 if (fSliderThrottler
== null) {
799 fSliderThrottler
= new SliderThrottler();
800 fSliderThrottler
.start();
806 * The selection event was sent by the viewer, refresh now.
808 //long rank = (long) (fTrace.getNbEvents() * ((double) fSlider.getSelection() / SLIDER_MAX));
810 if (fSlider
.getSelection() == 0 || fSlider
.getThumb() == SLIDER_MAX
) {
815 double ratio
= (double) fSlider
.getSelection() / (SLIDER_MAX
- fSlider
.getThumb());
816 double delta
= Math
.pow(10, -15);
818 while (fLines
.size() == 0) {
819 setTopPosition(ratio
);
823 delta
= Math
.min(delta
* 10, 0.1);
824 ratio
= Math
.max(ratio
- delta
, 0.0);
828 case SWT
.ARROW_DOWN
: {
829 if (fTopLineIndex
>= fLines
.size()) {
837 case SWT
.PAGE_DOWN
: {
838 fTopLineIndex
+= Math
.max(fNumVisibleLines
- 1, 1);
844 //if (fLines.size() == 0 || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
845 if (fLines
.size() == 0) {// || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
854 fTopLineIndex
-= Math
.max(fNumVisibleLines
- 1, 1);
860 //selectAndReveal(0);
865 //if (fTrace.getNbEvents() > 0) {
866 //selectAndReveal(fTrace.getNbEvents() - 1);
869 double delta
= Math
.pow(10, -15);
871 while (fLines
.size() == 0) {
872 setTopPosition(ratio
);
876 delta
= Math
.min(delta
* 10, 0.1);
877 ratio
= Math
.max(ratio
- delta
, 0.0);
884 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
885 if (e
.detail
!= SWT
.NONE
) {
886 fSlider
.setSelection((int) (SLIDER_MAX
* fTrace
.getLocationRatio(fLines
.get(fTopLineIndex
).location
)));
893 public void widgetDefaultSelected(SelectionEvent e
) {
896 // ------------------------------------------------------------------------
897 // MouseListener (Slider)
898 // ------------------------------------------------------------------------
904 public void mouseDown(MouseEvent e
) {
911 public void mouseUp(MouseEvent e
) {
916 * When the mouse button is released, perform the refresh immediately
917 * and interrupt and discard the slider throttler.
919 if (fSliderThrottler
!= null) {
920 fSliderThrottler
.interrupt();
921 fSliderThrottler
= null;
923 Event event
= new Event();
925 event
.detail
= SWT
.NONE
;
926 widgetSelected(new SelectionEvent(event
));
933 public void mouseDoubleClick(MouseEvent e
) {
936 // ------------------------------------------------------------------------
937 // KeyListener (StyledText)
938 // ------------------------------------------------------------------------
941 public void keyPressed(KeyEvent e
) {
942 if (fLines
.size() == 0) {
945 int caretOffset
= fStyledText
.getCaretOffset();
946 int previousCaretOffset
= getPreviousCaretOffset(e
.time
);
947 int previousLineAtCaretPosition
= fStyledText
.getLineAtOffset(previousCaretOffset
);
948 int previousColumnAtCaretPosition
= getPreviousCaretOffset(e
.time
) - fStyledText
.getOffsetAtLine(previousLineAtCaretPosition
);
950 case SWT
.ARROW_DOWN
: {
951 if (previousLineAtCaretPosition
< (fNumVisibleLines
- 2)) {
959 LineData lineData
= fLines
.get(fTopLineIndex
+ fStyledText
.getLineAtOffset(fStyledText
.getCaretOffset()));
960 if (!lineData
.location
.equals(fSelectedLocation
)) {
961 fSelectedLocation
= lineData
.location
;
962 refreshLineBackgrounds();
963 sendSelectionEvent(lineData
);
967 case SWT
.PAGE_DOWN
: {
968 if (previousLineAtCaretPosition
>= (fNumVisibleLines
- 1)) {
970 if (fLines
.get(fTopLineIndex
+ previousLineAtCaretPosition
).rank
% 2 == 0) {
971 fStyledText
.setLineBackground(previousLineAtCaretPosition
, 1, COLOR_BACKGROUND_EVEN
);
973 fStyledText
.setLineBackground(previousLineAtCaretPosition
, 1, COLOR_BACKGROUND_ODD
);
975 fSelectedLocation
= null;
976 fTopLineIndex
+= Math
.max(fNumVisibleLines
- 1, 1);
981 int line
= Math
.min(fNumVisibleLines
- 1, fStyledText
.getLineCount() - 1);
982 int offset
= fStyledText
.getOffsetAtLine(line
);
983 fStyledText
.setSelection(offset
+ Math
.min(previousColumnAtCaretPosition
, fLines
.get(fTopLineIndex
+ line
).string
.length()));
986 case SWT
.ARROW_RIGHT
: {
987 if (previousCaretOffset
< fStyledText
.getCharCount() || previousLineAtCaretPosition
< (fNumVisibleLines
- 2)) {
995 fStyledText
.setSelection(fStyledText
.getCaretOffset() + 1);
999 if (previousLineAtCaretPosition
> 0) {
1002 if (fLines
.size() == 0) {// || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
1010 LineData lineData
= fLines
.get(fTopLineIndex
);
1011 if (!lineData
.location
.equals(fSelectedLocation
)) {
1012 fSelectedLocation
= lineData
.location
;
1013 refreshLineBackgrounds();
1014 sendSelectionEvent(lineData
);
1016 fStyledText
.setSelection(caretOffset
);
1020 if (previousLineAtCaretPosition
> 0) {
1024 fTopLineIndex
-= Math
.max(fNumVisibleLines
- 1, 1);
1028 LineData lineData
= fLines
.get(fTopLineIndex
);
1029 if (!lineData
.location
.equals(fSelectedLocation
)) {
1030 fSelectedLocation
= lineData
.location
;
1031 refreshLineBackgrounds();
1032 sendSelectionEvent(lineData
);
1034 fStyledText
.setSelection(caretOffset
);
1037 case SWT
.ARROW_LEFT
: {
1038 if (previousCaretOffset
> 0) {
1041 if (fLines
.size() == 0) {// || (fTopLineIndex == 0 && fLines.get(0).rank == 0)) {
1044 long topRank
= fLines
.get(fTopLineIndex
).rank
;
1050 LineData lineData
= fLines
.get(fTopLineIndex
);
1051 if (!lineData
.location
.equals(fSelectedLocation
)) {
1052 fSelectedLocation
= lineData
.location
;
1053 refreshLineBackgrounds();
1054 sendSelectionEvent(lineData
);
1056 if (topRank
!= fLines
.get(fTopLineIndex
).rank
) {
1057 fStyledText
.setSelection(fLines
.get(fTopLineIndex
).string
.length());
1062 if ((e
.stateMask
& SWT
.CTRL
) == 0) {
1065 //selectAndReveal(0);
1066 setTopPosition(0.0);
1067 LineData lineData
= fLines
.get(fTopLineIndex
);
1068 if (!lineData
.location
.equals(fSelectedLocation
)) {
1069 fSelectedLocation
= lineData
.location
;
1070 refreshLineBackgrounds();
1071 sendSelectionEvent(lineData
);
1076 if ((e
.stateMask
& SWT
.CTRL
) == 0) {
1079 //if (fTrace.getNbEvents() > 0) {
1080 //selectAndReveal(fTrace.getNbEvents() - 1);
1083 double delta
= Math
.pow(10, -15);
1085 while (fLines
.size() == 0) {
1086 setTopPosition(ratio
);
1090 delta
= Math
.min(delta
* 10, 0.1);
1091 ratio
= Math
.max(ratio
- delta
, 0.0);
1093 LineData lineData
= fLines
.get(fTopLineIndex
);
1094 if (!lineData
.location
.equals(fSelectedLocation
)) {
1095 fSelectedLocation
= lineData
.location
;
1096 refreshLineBackgrounds();
1097 sendSelectionEvent(lineData
);
1104 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
1105 updateHighlightedRank();
1106 fSlider
.setSelection((int) (SLIDER_MAX
* fTrace
.getLocationRatio(fLines
.get(fTopLineIndex
).location
)));
1110 public void keyReleased(KeyEvent e
) {
1113 // ------------------------------------------------------------------------
1114 // CaretListener (StyledText)
1115 // ------------------------------------------------------------------------
1118 public void caretMoved(CaretEvent event
) {
1119 if (fHoldSelection
== 0) {
1120 int line
= fStyledText
.getLineAtOffset(event
.caretOffset
);
1121 if (fTopLineIndex
+ line
< fLines
.size()) {
1122 LineData lineData
= fLines
.get(fTopLineIndex
+ line
);
1123 if (!lineData
.location
.equals(fSelectedLocation
)) {
1124 fSelectedLocation
= lineData
.location
;
1125 refreshLineBackgrounds();
1126 sendSelectionEvent(lineData
);
1130 storeCaretPosition(event
.time
, event
.caretOffset
);
1131 if (fHoldSelection
== 0) {
1132 Point caret
= fStyledText
.getLocationAtOffset(fStyledText
.getCaretOffset());
1133 Point origin
= fScrolledComposite
.getOrigin();
1134 if (origin
.x
> caret
.x
) {
1136 } else if (caret
.x
- origin
.x
> fScrolledComposite
.getSize().x
) {
1137 origin
.x
= caret
.x
- fScrolledComposite
.getSize().x
+ 1;
1139 fScrolledComposite
.setOrigin(origin
);
1143 // ------------------------------------------------------------------------
1144 // MouseMoveListener (StyledText)
1145 // ------------------------------------------------------------------------
1148 public void mouseMove(MouseEvent e
) {
1149 fCursorYCoordinate
= e
.y
;
1150 if (e
.y
< 0 || e
.y
> fStyledText
.getSize().y
) {
1151 if (fHighlightedRank
!= Long
.MIN_VALUE
) {
1152 fHighlightedRank
= Long
.MIN_VALUE
;
1153 refreshLineBackgrounds();
1157 int offset
= fStyledText
.getOffsetAtLocation(new Point(0, e
.y
));
1158 int line
= fStyledText
.getLineAtOffset(offset
);
1159 if (line
< fLines
.size() - fTopLineIndex
) {
1160 LineData lineData
= fLines
.get(fTopLineIndex
+ line
);
1161 if (fHighlightedRank
!= lineData
.rank
) {
1162 fHighlightedRank
= lineData
.rank
;
1163 refreshLineBackgrounds();
1166 if (fHighlightedRank
!= Long
.MIN_VALUE
) {
1167 fHighlightedRank
= Long
.MIN_VALUE
;
1168 refreshLineBackgrounds();
1173 // ------------------------------------------------------------------------
1174 // MouseTrackListener (StyledText)
1175 // ------------------------------------------------------------------------
1178 public void mouseExit(MouseEvent e
) {
1179 fCursorYCoordinate
= -1;
1180 if (fHighlightedRank
!= Long
.MIN_VALUE
) {
1181 fHighlightedRank
= Long
.MIN_VALUE
;
1182 refreshLineBackgrounds();
1187 public void mouseEnter(MouseEvent e
) {
1188 fCursorYCoordinate
= e
.y
;
1192 public void mouseHover(MouseEvent e
) {
1195 // ------------------------------------------------------------------------
1196 // MouseWheelListener (StyledText)
1197 // ------------------------------------------------------------------------
1200 public void mouseScrolled(MouseEvent e
) {
1201 if (fLines
.size() == 0) {
1205 fTopLineIndex
-= e
.count
;
1209 //fSlider.setSelection((int) (SLIDER_MAX * ((double) fLines.get(fTopLineIndex).rank / fTrace.getNbEvents())));
1210 updateHighlightedRank();
1211 fSlider
.setSelection((int) (SLIDER_MAX
* fTrace
.getLocationRatio(fLines
.get(fTopLineIndex
).location
)));