//----------------------------------------------------------------------------
// 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);
}
}
}