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