//---------------------------------------------------------------------------- // COMPONENT NAME: LPEX Editor // // © Copyright IBM Corporation 2005, 2007 // All Rights Reserved. // // DESCRIPTION: // CursorHairline - sample SWT LPEX extension (cursor hairline) //---------------------------------------------------------------------------- package com.ibm.lpex.samples; import java.util.HashMap; import com.ibm.lpex.core.LpexView; import com.ibm.lpex.core.LpexViewAdapter; import com.ibm.lpex.core.LpexWindow; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Control; /** * Sample class to display a vertical hairline. * * <p>Installing this class in a document view adds a vertical hairline that * either tracks the cursor, or is fixed at the cursor location in effect when * it is installed.</p> * * <p>Here is the CursorHairline <a href="doc-files/CursorHairline.java.html">source * code</a>.</p> * * <p>A user profile (such as {@link TestUserProfile}) can install this feature * in a document view by calling, for example: * <pre> CursorHairline.install(lpexView, false);</pre></p> * * <p>The code fragment below sets the hairline at column 81 (assuming a fixed-pitch font): * <pre> // import com.ibm.lpex.samples.CursorHairline; * String currentPosition = lpexView.query("displayPosition"); * lpexView.doCommand("set displayPosition 81"); * CursorHairline.install(lpexView, false); * lpexView.doCommand("screenShow view"); * lpexView.doCommand("set displayPosition " + currentPosition);</pre></p> * * <p>See also {@link HairlineCommand} as an example of an editor command that * controls the display of the cursor hairline.</p> * * @see com.ibm.lpex.samples All the samples */ public class CursorHairline extends LpexViewAdapter implements PaintListener, DisposeListener { private static HashMap<LpexView,CursorHairline> _cursorHairlines = new HashMap<LpexView,CursorHairline>(); private LpexView _lpexView; private LpexWindow _lpexWindow; private String _originalCursorWidth; // track cursor / fixed hairline private boolean _trackCursor; // fixed hairline's x offset inside the text line private int _fixedPixelPosition; // latest x offset, y offset, and height inside the text window private int _pixelPosition, _height, _y; // hairline style, width, and color (TODO: make some customizable...) private static int _hairlineWidth = 1; private int _hairlineStyle = SWT.LINE_SOLID; private Color _hairlineColor; private int _R = 220, _G = 220, _B = 220; // light gray /** * Constructs a new cursor hairline for the specified view. */ private CursorHairline(LpexView lpexView) { _lpexView = lpexView; _lpexView.addLpexViewListener(this); _cursorHairlines.put(lpexView, this); } /** * Installs the hairline in the given document view. * * @param trackCursor true = hairline follows the cursor, or * false = fixed hairline at the current position */ public static void install(LpexView lpexView, boolean trackCursor) { if (lpexView != null) { CursorHairline ch = _cursorHairlines.get(lpexView); if (ch == null) { ch = new CursorHairline(lpexView); } ch._trackCursor = trackCursor; ch._fixedPixelPosition = -1; } } /** * Uninstalls the cursor hairline from the given view. */ public static void uninstall(LpexView lpexView) { CursorHairline ch = _cursorHairlines.get(lpexView); if (ch != null) { ch.uninstall(); } } // Removes this cursor hairline. private void uninstall() { if (_lpexView != null) { _lpexView.removeLpexViewListener(this); if (_lpexWindow != null) { Control textWindow = _lpexWindow.textWindow(); if (!textWindow.isDisposed()) { textWindow.removePaintListener(this); textWindow.removeDisposeListener(this); // if user is uninstalling us restore cursor width, erase hairline if (_originalCursorWidth != null) { _lpexView.doCommand("set cursor.width " + _originalCursorWidth); } textWindow.redraw(); } _lpexWindow = null; _hairlineColor.dispose(); } _cursorHairlines.remove(_lpexView); _lpexView = null; } } /** * View listener - the view has been refreshed. Installs our listeners as * soon as an LPEX window has been associated with the view. Assumes that * the specified document view will only ever be shown in this window. */ public void shown(LpexView lpexView) { // install our listeners, adjust cursor width if (_lpexWindow == null) { _lpexWindow = _lpexView.window(); if (_lpexWindow != null) { _hairlineColor = new Color(_lpexWindow.getDisplay(), _R, _G, _B); _lpexWindow.textWindow().addDisposeListener(this); _lpexWindow.textWindow().addPaintListener(this); } } if (_lpexWindow != null) { // always ensure minimum cursor width, so it doesn't disappear under hairline if (_lpexView.queryInt("current.cursor.width") == 1) { // record latest user setting _originalCursorWidth = _lpexView.query("cursor.width"); _lpexView.doCommand("set cursor.width 2"); } // determine the cursor pixel position int pixelPosition = (_trackCursor || _fixedPixelPosition == -1)? _lpexView.queryInt("pixelPosition") : _fixedPixelPosition; if (!_trackCursor && _fixedPixelPosition == -1) { _fixedPixelPosition = pixelPosition; } // adjust for the prefix area, expand/hide area, horizontal scroll if (pixelPosition >= 0) { pixelPosition += _lpexView.queryInt("prefixAreaWidth") + _lpexView.queryInt("expandHideAreaWidth") - _lpexView.queryInt("scroll"); } // determine the y offset and height int rowHeight = _lpexView.queryInt("rowHeight"); int rows = _lpexView.queryInt("rows"); int y = 0; int height = 0; if (rows > 0) { // adjust y offset for the top expand header if (_lpexView.elementOfRow(1) == 0) { y = rowHeight; } else { height = rowHeight; } // use only the element rows shown in the text area for (int i = 2; i <= rows && _lpexView.elementOfRow(i) != 0; i++) { height += rowHeight; } } // on changes, invalidate the areas covered by the old, new hairline if (pixelPosition != _pixelPosition || height != _height || y != _y) { Control textWindow = _lpexWindow.textWindow(); if (_height > 0) { textWindow.redraw(_pixelPosition, _y, _hairlineWidth, _height, false); } if (height > 0 && pixelPosition > 0) { textWindow.redraw(pixelPosition, y, _hairlineWidth, height, false); } _pixelPosition = pixelPosition; _height = height; _y = y; } } } /** * View listener - the view is being disposed. * Uninstalls the cursor hairline from this view. */ public void disposed(LpexView lpexView) { uninstall(); } /** * Text window dispose listener - the window is being disposed. * Uninstalls the cursor hairline. */ public void widgetDisposed(DisposeEvent e) { uninstall(); } /** * Text window paint listener - paint event notification. * Draws the cursor hairline. */ public void paintControl(PaintEvent e) { if (_lpexWindow != null && _height > 0 && _pixelPosition > 0) { e.gc.setForeground(_hairlineColor); e.gc.setLineStyle(_hairlineStyle); e.gc.setLineWidth(_hairlineWidth); e.gc.drawLine(_pixelPosition, _y, _pixelPosition, _y + _height); } } }