blob: a185c1c35fc681725de57c490096a01f22264a78 [file] [log] [blame]
package org.eclipse.swt.widgets;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved
*/
import org.eclipse.swt.internal.motif.*;
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 /*final*/ class Tracker extends Widget {
Composite parent;
Display display;
boolean tracking, stippled;
Rectangle [] rectangles = new Rectangle [0];
/**
* Creates a new instance of the widget.
*/
public Tracker (Composite parent, int style) {
super (parent, style);
this.parent = parent;
display = parent.getDisplay ();
}
/**
* Creates a new instance of the widget.
*/
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) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
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 () {
tracking = false;
}
void drawRectangles () {
if (parent != null) {
if (parent.isDisposed ()) return;
parent.getShell ().update ();
} else {
display.update ();
}
int xDisplay = display.xDisplay;
int color = OS.XWhitePixel (xDisplay, 0);
int xWindow = OS.XDefaultRootWindow (xDisplay);
if (parent != null) {
xWindow = OS.XtWindow (parent.handle);
if (xWindow == 0) return;
int [] argList = {OS.XmNforeground, 0, OS.XmNbackground, 0};
OS.XtGetValues (parent.handle, argList, argList.length / 2);
color = argList [1] ^ argList [3];
}
int gc = OS.XCreateGC (xDisplay, xWindow, 0, null);
OS.XSetForeground (xDisplay, gc, color);
OS.XSetSubwindowMode (xDisplay, gc, OS.IncludeInferiors);
OS.XSetFunction (xDisplay, gc, OS.GXxor);
int stipplePixmap = 0;
if (stippled) {
byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
stipplePixmap = OS.XCreateBitmapFromData (xDisplay, xWindow, bits, 8, 8);
OS.XSetStipple (xDisplay, gc, stipplePixmap);
OS.XSetFillStyle (xDisplay, gc, OS.FillStippled);
OS.XSetLineAttributes (xDisplay, gc, 3, OS.LineSolid, OS.CapButt, OS.JoinMiter);
}
for (int i=0; i<rectangles.length; i++) {
Rectangle rect = rectangles [i];
OS.XDrawRectangle (xDisplay, xWindow, gc, rect.x, rect.y, rect.width, rect.height);
}
if (stippled) {
OS.XFreePixmap (xDisplay, stipplePixmap);
}
OS.XFreeGC (xDisplay, gc);
}
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 () {
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 () {
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 () {
int xDisplay = display.xDisplay;
int color = OS.XWhitePixel (xDisplay, 0);
int xWindow = OS.XDefaultRootWindow (xDisplay);
if (parent != null) {
xWindow = OS.XtWindow (parent.handle);
if (xWindow == 0) return false;
}
boolean cancelled = false;
tracking = true;
drawRectangles ();
Event event = new Event ();
XAnyEvent xEvent = new XAnyEvent ();
int [] unused = new int [1];
int [] newX = new int [1], newY = new int [1], oldX = new int [1], oldY = new int [1];
int xtContext = OS.XtDisplayToApplicationContext (xDisplay);
OS.XQueryPointer (xDisplay, xWindow, unused, unused, oldX, oldY, unused, unused, unused);
while (tracking) {
if (parent != null && parent.isDisposed ()) break;
OS.XtAppNextEvent (xtContext, xEvent);
switch (xEvent.type) {
case OS.ButtonRelease:
case OS.MotionNotify:
OS.XQueryPointer (xDisplay, xWindow, unused, unused, newX, newY, unused, unused, unused);
if (oldX [0] != newX [0] || oldY [0] != newY [0]) {
drawRectangles ();
for (int i=0; i<rectangles.length; i++) {
rectangles [i].x += newX [0] - oldX [0];
rectangles [i].y += newY [0] - oldY [0];
}
sendEvent (SWT.Move);
drawRectangles ();
oldX [0] = newX [0]; oldY [0] = newY [0];
}
tracking = xEvent.type != OS.ButtonRelease;
break;
case OS.KeyPress:
XKeyEvent keyEvent = new XKeyEvent ();
OS.memmove (keyEvent, xEvent, XKeyEvent.sizeof);
if (keyEvent.keycode != 0) {
int [] keysym = new int [1];
OS.XLookupString (keyEvent, null, 0, keysym, unused);
keysym [0] &= 0xFFFF;
tracking = keysym [0] != OS.XK_Escape && keysym [0] != OS.XK_Cancel;
cancelled = !tracking;
}
break;
}
}
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) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
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) {
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) {
this.stippled = stippled;
}
}