blob: ed3fd83e46065a27ec38ebbd4fbcc060e19e15e3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2019 Angelo ZERR and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Angelo Zerr <angelo.zerr@gmail.com> - Bug 527720 - [CodeMining] Line number in vertical ruler can be not synchronized with line header annotation
* Thomas Wolf <thomas.wolf@paranor.ch> - Bug 553133
*******************************************************************************/
package org.eclipse.jface.text.source;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Consumer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.JFaceTextUtil;
/**
* Tracks line height changes of visible lines of a given {@link StyledText}.
*/
class VisibleLinesTracker implements PaintListener {
private static final String DATA_KEY= VisibleLinesTracker.class.getName();
/**
* The viewer to track.
*/
private final ITextViewer viewer;
private Map<Integer, Rectangle> oldVisibleLineBounds= Collections.emptyMap();
/**
* List of handler to call when a visible line height change.
*/
private final LinkedHashSet<Consumer<StyledText>> handlers= new LinkedHashSet<>();
/**
* Constructor to track line height change of visible lines of the {@link StyledText} of the
* given viewer.
*
* @param viewer the viewer to track
*/
private VisibleLinesTracker(ITextViewer viewer) {
this.viewer= viewer;
}
@Override
public void paintControl(PaintEvent e) {
StyledText textWidget= viewer.getTextWidget();
Map<Integer, Rectangle> newBounds= getVisibleLineBounds();
if (!oldVisibleLineBounds.equals(newBounds)) {
oldVisibleLineBounds= newBounds;
handlers.forEach(handler -> handler.accept(textWidget));
}
}
private Map<Integer, Rectangle> getVisibleLineBounds() {
StyledText textWidget= viewer.getTextWidget();
if (textWidget.isDisposed() || !textWidget.isVisible()) {
return Collections.emptyMap();
}
Map<Integer, Rectangle> res= new TreeMap<>();
int lastVisibleLineIndex= textWidget.getLineIndex(textWidget.getClientArea().height);
for (int widgetLine= textWidget.getLineIndex(0); widgetLine <= lastVisibleLineIndex; widgetLine++) {
int widgetLineOffset= textWidget.getOffsetAtLine(widgetLine);
res.put(Integer.valueOf(JFaceTextUtil.widgetLine2ModelLine(viewer, widgetLine)), //
new Rectangle(0, textWidget.getLinePixel(widgetLine), 0, textWidget.getLineHeight(widgetLineOffset)));
}
return res;
}
/**
* Track the line height change of the {@link StyledText} of the given handler an call the given
* handler.
*
* @param viewer the viewer to track
* @param handler the handler to call when line height change.
*/
static void track(ITextViewer viewer, Consumer<StyledText> handler) {
StyledText textWidget= viewer != null ? viewer.getTextWidget() : null;
if (textWidget == null) {
return;
}
VisibleLinesTracker tracker= (VisibleLinesTracker) textWidget.getData(DATA_KEY);
if (tracker == null) {
tracker= new VisibleLinesTracker(viewer);
textWidget.setData(DATA_KEY, tracker);
}
tracker.addHandler(handler);
}
/**
* Untrack the line height change of the {@link StyledText} of the given handler an call the
* given handler.
*
* @param viewer the viewer to track
* @param handler the handler to call when line height change.
*/
static void untrack(ITextViewer viewer, Consumer<StyledText> handler) {
StyledText textWidget= viewer != null ? viewer.getTextWidget() : null;
if (textWidget == null) {
return;
}
VisibleLinesTracker tracker= (VisibleLinesTracker) textWidget.getData(DATA_KEY);
if (tracker != null) {
tracker.removeHandler(handler);
}
}
/**
* Add the given handler.
*
* @param handler the handler to call when a visible line height change.
*/
private void addHandler(Consumer<StyledText> handler) {
if (handlers.isEmpty()) {
viewer.getTextWidget().addPaintListener(this);
}
handlers.add(handler);
}
/**
* Remove the given handler.
*
* @param handler the handler to call when a visible line height change.
*/
private void removeHandler(Consumer<StyledText> handler) {
handlers.remove(handler);
if (handlers.isEmpty()) {
viewer.getTextWidget().removePaintListener(this);
}
}
}