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