/** * Stabilize (do not allow scrolling) the JViewport displaying * the indicated JEditorPane. * This is typically used when the underlying document may change while * being edited in another view. The {@link #stop} method is used to release * the listeners and so unfreeze the viewport. *

This is a one shot class. The editor is expected to be good to go. * Only document changes are listened to. The first char of the top line is * pinned to the upper left corner. If needed, this could be extended * to pin the horizontal position as well. */ public static class FreezeViewport implements DocumentListener { private JEditorPane ep; private JViewport vp; private Document doc; private Position pos; private int topLine; private int nLine; public FreezeViewport(JEditorPane ep) { try { this.ep = ep; doc = ep.getDocument(); if(doc == null) return; vp = (JViewport)ep.getParent(); // may throw class cast, its ok Element root = doc.getDefaultRootElement(); nLine = root.getElementCount(); // Get the offset of the first displayed char in the top line Point pt = vp.getViewPosition(); int offset = ep.viewToModel(pt); // Determine the line number of the top displayed line topLine = root.getElementIndex(offset); // Note. offset may not be first char, due to horiz scroll // make offset the first char of the line offset = root.getElement(topLine).getStartOffset(); // Get marker to offset in the document pos = doc.createPosition(offset); doc.addDocumentListener(this); } catch (Exception ex) { // Note: did not start listener } } public void stop() { doc.removeDocumentListener(this); } private void track(DocumentEvent e) { // Might be able to use info from DocumentEvent to optimize try { Element root = doc.getDefaultRootElement(); int newNumLine = root.getElementCount(); // return if line count unchanged or changed after our mark if(nLine == newNumLine || e.getOffset() > pos.getOffset()) return; nLine = newNumLine; int newTopLine = root.getElementIndex(pos.getOffset()); if(topLine == newTopLine) return; topLine = newTopLine; // make a move int offset = root.getElement(topLine).getStartOffset(); Point pt = ep.modelToView(offset).getLocation(); pt.translate(-pt.x, 0); // x <-- 0, leave a few pixels to left vp.setViewPosition(pt); } catch(Exception ex) { stop(); } return; } public void insertUpdate(DocumentEvent e) { track(e); } public void removeUpdate(DocumentEvent e) { track(e); } public void changedUpdate(DocumentEvent e) { } }