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