blob: 9f38cd17dfa435d8a30b1571dd8ff95ed6da3f09 [file] [log] [blame]
package org.eclipse.swt.widgets;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved
*/
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
/**
* Instances of this class implement rubber banding rectangles.
*
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>(none)</dd>
* <dt><b>Events:</b></dt>
* <dd>Move</dd>
* </dl>
* <p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*/
public class Tracker extends Widget {
Control parent;
Display display;
boolean tracking, stippled;
Rectangle [] rectangles = new Rectangle [0];
public Tracker (Composite parent, int style) {
super (parent, style);
this.parent = parent;
display = parent.getDisplay ();
}
public Tracker (Display display, int style) {
if (display == null) display = Display.getCurrent ();
if (display == null) display = Display.getDefault ();
if (!display.isValidThread ()) {
error (SWT.ERROR_THREAD_INVALID_ACCESS);
}
this.style = style;
this.display = display;
}
/**
* Adds the listener to the collection of listeners who will
* be notified when the control is moved or resized, by sending
* it one of the messages defined in the <code>ControlListener</code>
* interface.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see ControlListener
* @see #removeControlListener
*/
public void addControlListener(ControlListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Move,typedListener);
}
/**
* Stop displaying the tracker rectangles.
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void close () {
checkWidget ();
tracking = false;
}
/**
* Draw the rectangles displayed by the tracker.
*/
void drawRectangles () {
if (parent != null) {
if (parent.isDisposed ()) return;
parent.getShell ().update ();
} else {
display.update ();
}
int bandWidth = 1;
int hwndTrack = OS.GetDesktopWindow ();
if (parent != null) hwndTrack = parent.handle;
int hDC = OS.GetDCEx (hwndTrack, 0, OS.DCX_CACHE);
int hBitmap = 0, hBrush = 0, oldBrush = 0;
if (stippled) {
bandWidth = 3;
byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits);
hBrush = OS.CreatePatternBrush (hBitmap);
oldBrush = OS.SelectObject (hDC, hBrush);
}
for (int i=0; i<rectangles.length; i++) {
Rectangle rect = rectangles [i];
OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATINVERT);
OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATINVERT);
}
if (stippled) {
OS.SelectObject (hDC, oldBrush);
OS.DeleteObject (hBrush);
OS.DeleteObject (hBitmap);
}
OS.ReleaseDC (hwndTrack, hDC);
}
public Display getDisplay () {
return display;
}
/**
* Returns the bounds of the Rectangles being drawn.
*
* @return the bounds of the Rectangles being drawn
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public Rectangle [] getRectangles () {
checkWidget ();
return rectangles;
}
/**
* Returns <code>true</code> if the rectangles are drawn with a stippled line, <code>false</code> otherwise.
*
* @return the stippled effect of the rectangles
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public boolean getStippled () {
checkWidget ();
return stippled;
}
/**
* Start displaying the Tracker rectangles.
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public boolean open () {
checkWidget ();
boolean cancelled = false;
tracking = true;
Event event = new Event ();
POINT pt = new POINT ();
MSG msg = new MSG ();
drawRectangles ();
int oldPos = OS.GetMessagePos ();
while (tracking && !cancelled) {
if (parent != null && parent.isDisposed ()) break;
OS.GetMessage (msg, 0, 0, 0);
switch (msg.message) {
case OS.WM_LBUTTONUP:
case OS.WM_MOUSEMOVE:
int newPos = OS.GetMessagePos ();
if (newPos != oldPos) {
int oldX = (short) (oldPos & 0xFFFF);
int oldY = (short) (oldPos >> 16);
int newX = (short) (newPos & 0xFFFF);
int newY = (short) (newPos >> 16);
drawRectangles ();
for (int i=0; i<rectangles.length; i++) {
rectangles [i].x += newX - oldX;
rectangles [i].y += newY - oldY;
}
/*
* It is possible (but unlikely), that application
* code could have disposed the widget in the move
* event. If this happens, return false to indicate
* that the tracking has failed.
*/
sendEvent (SWT.Move, event);
if (isDisposed ()) return false;
drawRectangles ();
oldPos = newPos;
}
tracking = msg.message != OS.WM_LBUTTONUP;
break;
case OS.WM_KEYDOWN:
cancelled = msg.wParam == OS.VK_ESCAPE;
break;
}
OS.DispatchMessage (msg);
}
drawRectangles ();
tracking = false;
return !cancelled;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the control is moved or resized.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see ControlListener
* @see #addControlListener
*/
public void removeControlListener (ControlListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
if (eventTable == null) return;
eventTable.unhook (SWT.Move, listener);
}
/**
* Specify the rectangles that should be drawn.
*
* @param rectangles the bounds of the rectangles to be drawn
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setRectangles (Rectangle [] rectangles) {
checkWidget ();
if (rectangles == null) error (SWT.ERROR_NULL_ARGUMENT);
this.rectangles = rectangles;
}
/**
* Change the appearance of the line used to draw the rectangles.
*
* @param stippled <code>true</code> if rectangle should appear stippled
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setStippled (boolean stippled) {
checkWidget ();
this.stippled = stippled;
}
}