blob: dd6b5d7d958a18d299472db0bb9d097aa9b97ba5 [file] [log] [blame]
package org.eclipse.swt.widgets;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved
*/
import org.eclipse.swt.internal.photon.*;
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 {
Composite parent;
Display display;
boolean tracking, stippled;
Rectangle [] rectangles = new Rectangle [0];
/**
* Constructs a new instance of this class given the display
* to create it on and a style value describing its behavior
* and appearance.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* for all SWT widget classes should include a comment which
* describes the style constants which are applicable to the class.
* </p><p>
* Note: Currently, null can be passed in for the display argument.
* This has the effect of creating the tracker on the currently active
* display if there is one. If there is no current display, the
* tracker is created on a "default" display. <b>Passing in null as
* the display argument is not considered to be good coding style,
* and may not be supported in a future release of SWT.</b>
* </p>
*
* @param display the display to create the tracker on
* @param style the style of control to construct
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*/
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;
}
/**
* Constructs a new instance of this class given its parent
* and a style value describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* for all SWT widget classes should include a comment which
* describes the style constants which are applicable to the class.
* </p>
*
* @param parent a widget which will be the parent of the new instance (cannot be null)
* @param style the style of widget to construct
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*
* @see SWT
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public Tracker (Composite parent, int style) {
super (parent, style);
this.parent = parent;
this.display = parent.getDisplay ();
}
/**
* 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;
}
void drawRectangles () {
if (parent != null) {
if (parent.isDisposed ()) return;
parent.getShell ().update ();
} else {
display.update ();
}
int rid = OS.Ph_DEV_RID;
if (parent != null) rid = OS.PtWidgetRid (parent.handle);
int phGC = OS.PgCreateGC (0);
if (phGC == 0) return;
int prevContext = OS.PgSetGC (phGC);
OS.PgSetRegion (rid);
OS.PgSetDrawMode (OS.Pg_DRAWMODE_XOR);
OS.PgSetFillColor (0xffffff);
int bandWidth = 0;
if (stippled) {
bandWidth = 2;
OS.PgSetFillTransPat (OS.Pg_PAT_HALF);
}
for (int i=0; i<rectangles.length; i++) {
Rectangle r = rectangles [i];
int x1 = r.x;
int y1 = r.y;
int x2 = r.x + r.width;
int y2 = r.y + r.height;
OS.PgDrawIRect(x1, y1, x2, y1 + bandWidth, OS.Pg_DRAW_FILL);
OS.PgDrawIRect(x1, y1 + bandWidth + 1, x1 + bandWidth, y2 - bandWidth - 1, OS.Pg_DRAW_FILL);
OS.PgDrawIRect(x2 - bandWidth, y1 + bandWidth + 1, x2, y2 - bandWidth - 1, OS.Pg_DRAW_FILL);
OS.PgDrawIRect(x1, y2 - bandWidth, x2, y2, OS.Pg_DRAW_FILL);
}
OS.PgSetGC (prevContext);
OS.PgDestroyGC (phGC);
}
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();
int sense = OS.Ph_EV_DRAG | OS.Ph_EV_KEY | OS.Ph_EV_BUT_PRESS |
OS.Ph_EV_BUT_RELEASE | OS.Ph_EV_PTR_MOTION;
int [] args = {
OS.Pt_ARG_WIDTH, 0, 0,
OS.Pt_ARG_HEIGHT, 0, 0,
OS.Pt_ARG_REGION_OPAQUE, 0, ~0,
OS.Pt_ARG_REGION_SENSE, sense, ~0,
OS.Pt_ARG_FILL_COLOR, OS.Pg_TRANSPARENT, 0,
};
OS.PtSetParentWidget (0);
int handle = OS.PtCreateWidget (OS.PtRegion (), 0, args.length / 3, args);
OS.PtRealizeWidget (handle);
PhRect_t rect = new PhRect_t ();
int rid = OS.PtWidgetRid (handle);
int input_group = OS.PhInputGroup (0);
OS.PhInitDrag (rid, OS.Ph_DRAG_KEY_MOTION | OS.Ph_TRACK_DRAG, rect, null, input_group, null, null, null, null, null);
PhCursorInfo_t info = new PhCursorInfo_t ();
OS.PhQueryCursor ((short)input_group, info);
int oldX = info.pos_x;
int oldY = info.pos_y;
int size = PhEvent_t.sizeof + 1024;
int buffer = OS.malloc (size);
PhEvent_t event = new PhEvent_t ();
Event ev = new Event ();
drawRectangles ();
boolean tracking = true;
boolean cancelled = false;
while (tracking && !cancelled) {
if (parent != null && parent.isDisposed ()) break;
int result = OS.PhEventNext (buffer, size);
switch (result) {
case OS.Ph_EVENT_MSG: break;
case OS.Ph_RESIZE_MSG:
size = OS.PhGetMsgSize (buffer);
OS.free (buffer);
buffer = OS.malloc (size);
continue;
}
OS.memmove (event, buffer, PhEvent_t.sizeof);
if (event.type == OS.Ph_EV_DRAG) {
switch (event.subtype) {
case OS.Ph_EV_DRAG_MOTION_EVENT: {
int data = OS.PhGetData (buffer);
if (data == 0) break;
PhPointerEvent_t pe = new PhPointerEvent_t ();
OS.memmove (pe, data, PhPointerEvent_t.sizeof);
int newX = pe.pos_x;
int newY = pe.pos_y;
if (newX != oldX || newY != oldY) {
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.
*/
ev.x = newX;
ev.y = newY;
sendEvent (SWT.Move, ev);
if (isDisposed ()) return false;
drawRectangles ();
oldX = newX;
oldY = newY;
}
break;
}
case OS.Ph_EV_DRAG_KEY_EVENT: {
int data = OS.PhGetData (buffer);
if (data == 0) break;
PhKeyEvent_t ke = new PhKeyEvent_t ();
OS.memmove (ke, data, PhKeyEvent_t.sizeof);
if ((ke.key_flags & OS.Pk_KF_Sym_Valid) != 0) {
cancelled = ke.key_sym == OS.Pk_Escape;
}
break;
}
case OS.Ph_EV_DRAG_COMPLETE: {
tracking = false;
break;
}
}
}
OS.PtEventHandler (buffer);
}
drawRectangles ();
tracking = false;
OS.PtDestroyWidget (handle);
return !cancelled;
}
void releaseWidget () {
super.releaseWidget ();
parent = null;
display = null;
rectangles = null;
}
/**
* 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);
}
public void setCursor (Cursor value) {
}
/**
* 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;
}
}