blob: 860eb41b9d1a8c1885e83edcaf953f60ecae2683 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2012 Innoopract Informationssysteme GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
* Frank Appel - replaced singletons and static fields (Bug 337787)
******************************************************************************/
package org.eclipse.swt.widgets;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.rap.rwt.Adaptable;
import org.eclipse.rap.rwt.internal.application.ApplicationContext;
import org.eclipse.rap.rwt.internal.application.ApplicationContextUtil;
import org.eclipse.rap.rwt.internal.application.RWTFactory;
import org.eclipse.rap.rwt.internal.lifecycle.CurrentPhase;
import org.eclipse.rap.rwt.internal.lifecycle.IUIThreadHolder;
import org.eclipse.rap.rwt.internal.lifecycle.LifeCycle;
import org.eclipse.rap.rwt.internal.lifecycle.LifeCycleUtil;
import org.eclipse.rap.rwt.internal.protocol.ProtocolUtil;
import org.eclipse.rap.rwt.internal.service.ContextProvider;
import org.eclipse.rap.rwt.internal.service.ServletLog;
import org.eclipse.rap.rwt.internal.theme.QxColor;
import org.eclipse.rap.rwt.internal.theme.QxImage;
import org.eclipse.rap.rwt.internal.theme.QxType;
import org.eclipse.rap.rwt.internal.theme.SimpleSelector;
import org.eclipse.rap.rwt.internal.theme.ThemeUtil;
import org.eclipse.rap.rwt.internal.uicallback.UICallBackManager;
import org.eclipse.rap.rwt.lifecycle.ILifeCycleAdapter;
import org.eclipse.rap.rwt.lifecycle.IWidgetAdapter;
import org.eclipse.rap.rwt.lifecycle.PhaseId;
import org.eclipse.rap.rwt.lifecycle.ProcessActionRunner;
import org.eclipse.rap.rwt.lifecycle.UICallBack;
import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.rap.rwt.service.ISessionStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.SerializableCompatibility;
import org.eclipse.swt.internal.events.EventList;
import org.eclipse.swt.internal.events.EventUtil;
import org.eclipse.swt.internal.widgets.IDisplayAdapter;
import org.eclipse.swt.internal.widgets.WidgetAdapter;
import org.eclipse.swt.internal.widgets.WidgetTreeVisitor;
import org.eclipse.swt.internal.widgets.WidgetTreeVisitor.AllWidgetTreeVisitor;
/**
* Instances of this class are responsible for managing the
* connection between SWT and the underlying operating
* system. Their most important function is to implement
* the SWT event loop in terms of the platform event model.
* They also provide various methods for accessing information
* about the operating system, and have overall control over
* the operating system resources which SWT allocates.
* <p>
* Applications which are built with SWT will <em>almost always</em>
* require only a single display. In particular, some platforms
* which SWT supports will not allow more than one <em>active</em>
* display. In other words, some platforms do not support
* creating a new display if one already exists that has not been
* sent the <code>dispose()</code> message.
* <p>
* In SWT, the thread which creates a <code>Display</code>
* instance is distinguished as the <em>user-interface thread</em>
* for that display.
* </p>
* The user-interface thread for a particular display has the
* following special attributes:
* <ul>
* <li>
* The event loop for that display must be run from the thread.
* </li>
* <li>
* Some SWT API methods (notably, most of the public methods in
* <code>Widget</code> and its subclasses), may only be called
* from the thread. (To support multi-threaded user-interface
* applications, class <code>Display</code> provides inter-thread
* communication methods which allow threads other than the
* user-interface thread to request that it perform operations
* on their behalf.)
* </li>
* <li>
* The thread is not allowed to construct other
* <code>Display</code>s until that display has been disposed.
* (Note that, this is in addition to the restriction mentioned
* above concerning platform support for multiple displays. Thus,
* the only way to have multiple simultaneously active displays,
* even on platforms which support it, is to have multiple threads.)
* </li>
* </ul>
* Enforcing these attributes allows SWT to be implemented directly
* on the underlying operating system's event model. This has
* numerous benefits including smaller footprint, better use of
* resources, safer memory management, clearer program logic,
* better performance, and fewer overall operating system threads
* required. The down side however, is that care must be taken
* (only) when constructing multi-threaded applications to use the
* inter-thread communication mechanisms which this class provides
* when required.
* </p><p>
* All SWT API methods which may only be called from the user-interface
* thread are distinguished in their documentation by indicating that
* they throw the "<code>ERROR_THREAD_INVALID_ACCESS</code>"
* SWT exception.
* </p>
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>(none)</dd>
* <dt><b>Events:</b></dt>
* <dd>Close, Dispose, Skin</dd>
* </dl>
* <p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
* @see #syncExec
* @see #asyncExec
* @see #wake
*
* @see #readAndDispatch
* @see #sleep
* @see Device#dispose
*
* @since 1.0
*/
public class Display extends Device implements Adaptable {
private final static String BOUNDS = "bounds";
private static final String ATTR_INVALIDATE_FOCUS
= DisplayAdapter.class.getName() + "#invalidateFocus";
private static final String APP_NAME = Display.class.getName() + "#appName";
private static final String APP_VERSION = Display.class.getName() + "#appVersion";
/* Package Name */
static final String PACKAGE_PREFIX = "org.eclipse.swt.widgets.";
// Keep in sync with client-side (EventUtil.js)
private static final int DOUBLE_CLICK_TIME = 500;
private static final int GROW_SIZE = 1024;
/**
* Returns the display which the currently running thread is
* the user-interface thread for, or null if the currently
* running thread is not a user-interface thread for any display.
*
* @return the current display
*/
public static Display getCurrent() {
Display result = LifeCycleUtil.getSessionDisplay();
if( result != null ) {
if( result.isDisposed() || result.getThread() != Thread.currentThread() ) {
result = null;
}
}
return result;
}
/**
* Returns the default display. One is created if it did not already exist.
*
* <p><strong>Note:</strong> In RWT, a new display is only created if the
* calling thread is the user-interface thread.
* </p>
*
* @return the default display
*/
public static Display getDefault() {
Display display = LifeCycleUtil.getSessionDisplay();
if( display == null || display.isDisposed() ) {
if( isUIThread() ) {
display = new Display();
}
}
return display;
}
private static boolean isUIThread() {
boolean result = false;
if( ContextProvider.hasContext() ) {
IUIThreadHolder uiThreadHolder = LifeCycleUtil.getUIThread( ContextProvider.getSessionStore() );
Thread uiThread = uiThreadHolder == null ? null : uiThreadHolder.getThread();
result = uiThread == Thread.currentThread();
}
return result;
}
private final List<Shell> shells;
private transient Thread thread;
private final ISessionStore sessionStore;
private final Rectangle bounds;
private final Point cursorLocation;
private Shell activeShell;
private Collection<Control> redrawControls;
private Control focusControl;
private EventTable filterTable;
private EventTable eventTable;
private transient Monitor monitor;
private transient IDisplayAdapter displayAdapter;
private WidgetAdapter widgetAdapter;
private Runnable[] disposeList;
private Composite[] layoutDeferred;
private int layoutDeferredCount;
private Widget[] skinList;
private int skinCount;
private boolean beep;
/* Display Data */
private Object data;
private String[] keys;
private Object[] values;
private Synchronizer synchronizer;
private TimerExecScheduler scheduler;
/**
* Constructs a new instance of this class.
* <p>
* Note: The resulting display is marked as the <em>current</em>
* display. If this is the first display which has been
* constructed since the application started, it is also
* marked as the <em>default</em> display.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if called from a thread that already created an existing display</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*
* @see #getCurrent
* @see #getDefault
* @see Widget#checkSubclass
* @see Shell
*/
public Display() {
if( getCurrent() != null ) {
SWT.error( SWT.ERROR_NOT_IMPLEMENTED, null, " [multiple displays]" );
}
LifeCycleUtil.setSessionDisplay( this );
attachThread();
sessionStore = ContextProvider.getSessionStore();
shells = new ArrayList<Shell>();
monitor = new Monitor( this );
cursorLocation = new Point( 0, 0 );
bounds = readInitialBounds();
synchronizer = new Synchronizer( this );
register();
}
/**
* Returns a rectangle describing the receiver's size and location.
*
* @return the bounding rectangle
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
@Override
public Rectangle getBounds() {
checkDevice();
return new Rectangle( bounds.x, bounds.y, bounds.width, bounds.height );
}
/**
* Returns the control which currently has keyboard focus,
* or null if keyboard events are not currently going to
* any of the controls built by the currently running
* application.
*
* @return the control under the cursor
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Control getFocusControl() {
checkDevice();
return focusControl;
}
private void setFocusControl( Control focusControl ) {
if( this.focusControl != focusControl ) {
if( this.focusControl != null && !this.focusControl.isInDispose() ) {
Control currentFocusControl = this.focusControl;
Shell shell = currentFocusControl.getShell();
currentFocusControl.notifyListeners( SWT.FocusOut, new Event() );
shell.updateDefaultButton( currentFocusControl, false );
}
this.focusControl = focusControl;
if( this.focusControl != null ) {
Control currentFocusControl = this.focusControl;
Shell shell = currentFocusControl.getShell();
currentFocusControl.notifyListeners( SWT.FocusIn, new Event() );
shell.updateDefaultButton( currentFocusControl, true );
}
}
}
/////////////////////
// Coordinate mapping
/**
* Maps a point from one coordinate system to another.
* When the control is null, coordinates are mapped to
* the display.
* <p>
* NOTE: On right-to-left platforms where the coordinate
* systems are mirrored, special care needs to be taken
* when mapping coordinates from one control to another
* to ensure the result is correctly mirrored.
*
* Mapping a point that is the origin of a rectangle and
* then adding the width and height is not equivalent to
* mapping the rectangle. When one control is mirrored
* and the other is not, adding the width and height to a
* point that was mapped causes the rectangle to extend
* in the wrong direction. Mapping the entire rectangle
* instead of just one point causes both the origin and
* the corner of the rectangle to be mapped.
* </p>
*
* @param from the source <code>Control</code> or <code>null</code>
* @param to the destination <code>Control</code> or <code>null</code>
* @param point to be mapped
* @return point with mapped coordinates
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the point is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Point map( Control from, Control to, Point point ) {
checkDevice();
if( point == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
return map( from, to, point.x, point.y );
}
/**
* Maps a point from one coordinate system to another.
* When the control is null, coordinates are mapped to
* the display.
* <p>
* NOTE: On right-to-left platforms where the coordinate
* systems are mirrored, special care needs to be taken
* when mapping coordinates from one control to another
* to ensure the result is correctly mirrored.
*
* Mapping a point that is the origin of a rectangle and
* then adding the width and height is not equivalent to
* mapping the rectangle. When one control is mirrored
* and the other is not, adding the width and height to a
* point that was mapped causes the rectangle to extend
* in the wrong direction. Mapping the entire rectangle
* instead of just one point causes both the origin and
* the corner of the rectangle to be mapped.
* </p>
*
* @param from the source <code>Control</code> or <code>null</code>
* @param to the destination <code>Control</code> or <code>null</code>
* @param x coordinates to be mapped
* @param y coordinates to be mapped
* @return point with mapped coordinates
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Point map( Control from, Control to, int x, int y ) {
checkDevice();
Rectangle rectangle = map( from, to, x, y, 0, 0 );
return new Point( rectangle.x, rectangle.y );
}
/**
* Maps a point from one coordinate system to another.
* When the control is null, coordinates are mapped to
* the display.
* <p>
* NOTE: On right-to-left platforms where the coordinate
* systems are mirrored, special care needs to be taken
* when mapping coordinates from one control to another
* to ensure the result is correctly mirrored.
*
* Mapping a point that is the origin of a rectangle and
* then adding the width and height is not equivalent to
* mapping the rectangle. When one control is mirrored
* and the other is not, adding the width and height to a
* point that was mapped causes the rectangle to extend
* in the wrong direction. Mapping the entire rectangle
* instead of just one point causes both the origin and
* the corner of the rectangle to be mapped.
* </p>
*
* @param from the source <code>Control</code> or <code>null</code>
* @param to the destination <code>Control</code> or <code>null</code>
* @param rectangle to be mapped
* @return rectangle with mapped coordinates
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Rectangle map( Control from, Control to, Rectangle rectangle ) {
checkDevice();
if( rectangle == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
return map( from,
to,
rectangle.x,
rectangle.y,
rectangle.width,
rectangle.height );
}
/**
* Maps a point from one coordinate system to another.
* When the control is null, coordinates are mapped to
* the display.
* <p>
* NOTE: On right-to-left platforms where the coordinate
* systems are mirrored, special care needs to be taken
* when mapping coordinates from one control to another
* to ensure the result is correctly mirrored.
*
* Mapping a point that is the origin of a rectangle and
* then adding the width and height is not equivalent to
* mapping the rectangle. When one control is mirrored
* and the other is not, adding the width and height to a
* point that was mapped causes the rectangle to extend
* in the wrong direction. Mapping the entire rectangle
* instead of just one point causes both the origin and
* the corner of the rectangle to be mapped.
* </p>
*
* @param from the source <code>Control</code> or <code>null</code>
* @param to the destination <code>Control</code> or <code>null</code>
* @param x coordinates to be mapped
* @param y coordinates to be mapped
* @param width coordinates to be mapped
* @param height coordinates to be mapped
* @return rectangle with mapped coordinates
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Rectangle map( Control from, Control to, int x, int y, int width, int height ) {
checkDevice();
int newX = x;
int newY = y;
if( from != null ) {
Point fromOrigin = getAbsoluteOrigin( from );
newX += fromOrigin.x;
newY += fromOrigin.y;
}
if( to != null ) {
Point toOrigin = getAbsoluteOrigin( to );
newX -= toOrigin.x;
newY -= toOrigin.y;
}
return new Rectangle( newX, newY, width, height );
}
/*
* Returns the origin of the coordinate system of a given control in absolute
* coordinates, i.e. relative to the display.
*/
private static Point getAbsoluteOrigin( Control control ) {
Control currentControl = control;
Point absolute = new Point( 0, 0 );
while( currentControl != null ) {
Point origin = getOrigin( currentControl );
absolute.x += origin.x;
absolute.y += origin.y;
if( currentControl instanceof Shell ) {
currentControl = null;
} else {
currentControl = currentControl.getParent();
}
}
return new Point( absolute.x, absolute.y );
}
/*
* Returns the origin of the coordinate system of a given control, relative to
* it's parent or, if it does not have a parent, relative to the display.
*/
private static Point getOrigin( Control control ) {
Point result = control.getLocation();
// Due the way that the qx client implementation works, the coordinate
// system of composites starts at the inner edge of their border and thus
// need to be offset by the border width.
// Since only composites can contain child widgets, only they need this
// correction. This implementation seems to be a good fit with SWT.
if( control instanceof Composite ) {
int borderWidth = control.getBorderWidth();
result.x += borderWidth;
result.y += borderWidth;
}
return result;
}
///////////
// Listener
/**
* Adds the listener to the collection of listeners who will
* be notified when an event of the given type occurs. The event
* type is one of the event constants defined in class <code>SWT</code>.
* When the event does occur in the display, the listener is notified by
* sending it the <code>handleEvent()</code> message.
*
* @param eventType the type of event to listen for
* @param listener the listener which should be notified when the event occurs
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see Listener
* @see SWT
* @see #removeListener
*
* @since 1.3
*/
public void addListener( int eventType, Listener listener ) {
checkDevice();
if( listener == null ) {
error( SWT.ERROR_NULL_ARGUMENT );
}
if( eventTable == null ) {
eventTable = new EventTable();
}
eventTable.hook( eventType, listener );
}
/**
* Removes the listener from the collection of listeners who will
* be notified when an event of the given type occurs. The event type
* is one of the event constants defined in class <code>SWT</code>.
*
* @param eventType the type of event to listen for
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see Listener
* @see SWT
* @see #addListener
*
* @since 1.3
*/
public void removeListener( int eventType, Listener listener ) {
checkDevice();
if( listener == null ) {
error( SWT.ERROR_NULL_ARGUMENT );
}
if( eventTable != null ) {
eventTable.unhook( eventType, listener );
}
}
void sendEvent( int eventType, Event event ) {
event.display = this;
event.type = eventType;
if( event.time == 0 ) {
event.time = EventUtil.getLastEventTime();
}
filterEvent( event );
if( eventTable != null ) {
eventTable.sendEvent( event );
}
}
//////////
// Dispose
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread just before the
* receiver is disposed. Specifying a <code>null</code> runnable
* is ignored.
*
* @param runnable code to run at dispose time.
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public void disposeExec( Runnable runnable ) {
checkDevice();
if (disposeList == null) disposeList = new Runnable [4];
for (int i=0; i<disposeList.length; i++) {
if (disposeList [i] == null) {
disposeList [i] = runnable;
return;
}
}
Runnable [] newDisposeList = new Runnable [disposeList.length + 4];
System.arraycopy (disposeList, 0, newDisposeList, 0, disposeList.length);
newDisposeList [disposeList.length] = runnable;
disposeList = newDisposeList;
}
/**
* Requests that the connection between SWT and the underlying
* operating system be closed.
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see Device#dispose
*
* @since 1.3
*/
public void close() {
checkDevice();
Event event = new Event();
sendEvent( SWT.Close, event );
if( event.doit ) {
dispose();
}
}
@Override
protected void release() {
sendDisposeEvent();
disposeShells();
runDisposeExecs();
synchronizer.releaseSynchronizer();
if( scheduler != null ) {
scheduler.dispose();
}
filterTable = null;
eventTable = null;
}
@Override
protected void destroy() {
deregister();
}
private void sendDisposeEvent() {
sendEvent( SWT.Dispose, new Event() );
}
private void disposeShells() {
Shell[] shells = getShells();
for( int i = 0; i < shells.length; i++ ) {
Shell shell = shells[ i ];
try {
shell.dispose();
} catch( Throwable thr ) {
ServletLog.log( "Exception while disposing shell: " + shell, thr );
}
}
// TODO [rh] consider dispatching pending messages (e.g. asyncExec)
// while( readAndDispatch() ) {}
}
private void runDisposeExecs() {
checkDevice();
if( disposeList != null ) {
for( int i = 0; i < disposeList.length; i++ ) {
if( disposeList[ i ] != null ) {
try {
disposeList[ i ].run();
} catch( Throwable thr ) {
String msg = "Exception while executing dispose-runnable.";
ServletLog.log( msg, thr );
}
}
}
}
}
/////////////////////
// Adaptable override
@SuppressWarnings("unchecked")
public <T> T getAdapter( Class<T> adapter ) {
T result = null;
if( adapter == IDisplayAdapter.class ) {
if( displayAdapter == null ) {
displayAdapter = new DisplayAdapter();
}
result = ( T )displayAdapter;
} else if( adapter == RemoteObject.class || adapter == IWidgetAdapter.class ) {
if( widgetAdapter == null ) {
widgetAdapter = new WidgetAdapter( "w1" );
}
result = ( T )widgetAdapter;
} else if( adapter == ILifeCycleAdapter.class ) {
result = ( T )getApplicationContext().getLifeCycleAdapterFactory().getAdapter( this );
}
return result;
}
private ApplicationContext getApplicationContext() {
return ApplicationContextUtil.get( sessionStore );
}
///////////////////
// Shell management
/**
* Returns a (possibly empty) array containing all shells which have
* not been disposed and have the receiver as their display.
*
* @return the receiver's shells
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Shell[] getShells() {
checkDevice();
Shell[] result = new Shell[ shells.size() ];
shells.toArray( result );
return result;
}
/**
* Returns the currently active <code>Shell</code>, or null
* if no shell belonging to the currently running application
* is active.
*
* @return the active shell or null
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Shell getActiveShell() {
checkDevice();
return activeShell;
}
final void setActiveShell( Shell activeShell ) {
checkDevice();
if( this.activeShell != activeShell ) {
Shell lastActiveShell = this.activeShell;
if( this.activeShell != null ) {
this.activeShell.saveFocus();
}
// Move active shell to end of list to maintain correct z-order
if( activeShell != null ) {
shells.remove( activeShell );
shells.add( activeShell );
}
if( lastActiveShell != null && ( lastActiveShell.state & Widget.DISPOSE_SENT ) == 0 ) {
lastActiveShell.notifyListeners( SWT.Deactivate, new Event() );
}
this.activeShell = activeShell;
if( activeShell != null ) {
activeShell.notifyListeners( SWT.Activate, new Event() );
}
if( this.activeShell != null ) {
this.activeShell.restoreFocus();
}
}
}
final void addShell( Shell shell ) {
shells.add( shell );
}
final void removeShell( Shell shell ) {
shells.remove( shell );
if( shell == activeShell ) {
int counter = 0;
Shell lastVisibleShell = null;
while( lastVisibleShell == null && counter < shells.size() ) {
counter++;
Shell currentShell = shells.get( shells.size() - counter );
if( currentShell.isVisible() ) {
lastVisibleShell = currentShell;
}
}
setActiveShell( lastVisibleShell );
}
}
////////////////////
// Thread management
/**
* Returns the user-interface thread for the receiver. Note that the
* user-interface thread may change per user-request.
*
* @return the receiver's user-interface thread or null if there's no
* current user-request executed that belongs to the display.
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*/
public Thread getThread() {
synchronized( deviceLock ) {
if( isDisposed() ) {
error( SWT.ERROR_DEVICE_DISPOSED );
}
return thread;
}
}
private void attachThread() {
thread = Thread.currentThread();
}
private void detachThread() {
thread = null;
}
//////////////////////
// Information methods
/**
* Returns the display which the given thread is the
* user-interface thread for, or null if the given thread
* is not a user-interface thread for any display. Specifying
* <code>null</code> as the thread will return <code>null</code>
* for the display.
*
* @param thread the user-interface thread
* @return the display for the given thread
*
* @since 1.3
*/
public static Display findDisplay( Thread thread ) {
synchronized( Device.class ) {
WeakReference[] displays = getDisplays();
Display result = null;
for( int i = 0; result == null && i < displays.length; i++ ) {
WeakReference current = displays[ i ];
if( current != null ) {
Display display = ( Display )current.get();
if( display != null && !display.isDisposed() && display.thread == thread ) {
result = display;
}
}
}
return result;
}
}
/**
* Sets the synchronizer used by the display to be
* the argument, which can not be null.
*
* @param synchronizer the new synchronizer for the display (must not be null)
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the synchronizer is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_FAILED_EXEC - if an exception occurred while running an inter-thread message</li>
* </ul>
*
* @since 1.3
*/
// verbatim copy of SWT code
public void setSynchronizer (Synchronizer synchronizer) {
checkDevice ();
if (synchronizer == null) error (SWT.ERROR_NULL_ARGUMENT);
if (synchronizer == this.synchronizer) return;
Synchronizer oldSynchronizer;
synchronized (deviceLock) {
oldSynchronizer = this.synchronizer;
this.synchronizer = synchronizer;
}
if (oldSynchronizer != null) {
oldSynchronizer.runAsyncMessages(true);
}
}
/**
* Gets the synchronizer used by the display.
*
* @return the receiver's synchronizer
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public Synchronizer getSynchronizer() {
checkDevice ();
return synchronizer;
}
/**
* Returns the thread that has invoked <code>syncExec</code>
* or null if no such runnable is currently being invoked by
* the user-interface thread.
* <p>
* Note: If a runnable invoked by asyncExec is currently
* running, this method will return null.
* </p>
*
* @return the receiver's sync-interface thread
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public Thread getSyncThread () {
synchronized( deviceLock ) {
if( isDisposed() ) {
error( SWT.ERROR_DEVICE_DISPOSED );
}
return synchronizer.syncThread;
}
}
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread at the next
* reasonable opportunity. Note that the user-interface thread may change
* per user-request. The caller of this method continues
* to run in parallel, and is not notified when the
* runnable has completed. Specifying <code>null</code> as the
* runnable simply wakes the user-interface thread when run.
* <p>
* Note that at the time the runnable is invoked, widgets
* that have the receiver as their display may have been
* disposed. Therefore, it is necessary to check for this
* case inside the runnable before accessing the widget.
* </p>
*
* @param runnable code to run on the user-interface thread or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #syncExec
*/
public void asyncExec( Runnable runnable ) {
synchronized( deviceLock ) {
if( isDisposed() ) {
error( SWT.ERROR_DEVICE_DISPOSED );
}
synchronizer.asyncExec( runnable );
}
}
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread at the next
* reasonable opportunity. Note that the user-interface thread may change
* per user-request. The thread which calls this method
* is suspended until the runnable completes. Specifying <code>null</code>
* as the runnable simply wakes the user-interface thread.
* <p>
* Note that at the time the runnable is invoked, widgets
* that have the receiver as their display may have been
* disposed. Therefore, it is necessary to check for this
* case inside the runnable before accessing the widget.
* </p>
*
* @param runnable code to run on the user-interface thread or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_FAILED_EXEC - if an exception occured when executing the runnable</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #asyncExec
*/
public void syncExec( Runnable runnable ) {
Synchronizer synchronizer;
synchronized( deviceLock ) {
if( isDisposed() ) {
error( SWT.ERROR_DEVICE_DISPOSED );
}
synchronizer = this.synchronizer;
}
synchronizer.syncExec( runnable );
}
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread after the specified
* number of milliseconds have elapsed. If milliseconds is less
* than zero, the runnable is not executed.
* <p>
* Note that at the time the runnable is invoked, widgets
* that have the receiver as their display may have been
* disposed. Therefore, it is necessary to check for this
* case inside the runnable before accessing the widget.
* </p>
*
* @param milliseconds the delay before running the runnable
* @param runnable code to run on the user-interface thread
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the runnable is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #asyncExec
* @since 1.2
*/
public void timerExec( int milliseconds, Runnable runnable ) {
checkDevice();
if( runnable == null ) {
error( SWT.ERROR_NULL_ARGUMENT );
}
if( scheduler == null ) {
scheduler = new TimerExecScheduler( this, UICallBackManager.getInstance() );
}
if( milliseconds < 0 ) {
scheduler.cancel( runnable );
} else {
scheduler.schedule( milliseconds, runnable );
}
}
/**
* Reads an event from the <!-- operating system's --> event queue,
* dispatches it appropriately, and returns <code>true</code>
* if there is potentially more work to do, or <code>false</code>
* if the caller can sleep until another event is placed on
* the event queue.
* <p>
* In addition to checking the system event queue, this method also
* checks if any inter-thread messages (created by <code>syncExec()</code>
* or <code>asyncExec()</code>) are waiting to be processed, and if
* so handles them before returning.
* </p>
*
* @return <code>false</code> if the caller can sleep upon return from this method
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_FAILED_EXEC - if an exception occurred while running an inter-thread message</li>
* </ul>
*
* @see #sleep
* @see #wake
*
* @since 1.1
*/
public boolean readAndDispatch() {
checkDevice();
runSkin();
runDeferredLayouts();
return runPendingMessages();
}
private boolean runPendingMessages() {
boolean result = false;
if( PhaseId.PREPARE_UI_ROOT.equals( CurrentPhase.get() )
|| PhaseId.PROCESS_ACTION.equals( CurrentPhase.get() ) )
{
result = ProcessActionRunner.executeNext();
if( !result ) {
result = executeNextEvent();
}
if( !result ) {
result = synchronizer.runAsyncMessages( false );
}
if( !result ) {
result = executeNextRedraw();
}
}
return result;
}
private boolean executeNextEvent() {
boolean result = false;
Event[] events = EventList.getInstance().getAll();
while( !result && events.length > 0 ) {
Event event = events[ 0 ];
EventList.getInstance().remove( event );
if( EventUtil.allowProcessing( event ) ) {
event.widget.notifyListeners( event.type, event );
result = true;
} else {
events = EventList.getInstance().getAll();
}
}
return result;
}
/**
* Causes the user-interface thread to <em>sleep</em> (that is,
* to be put in a state where it does not consume CPU cycles)
* until an event is received or it is otherwise awakened.
*
* @return <code>true</code> if an event requiring dispatching was placed on the queue.
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #wake
*
* @since 1.1
*/
public boolean sleep() {
checkDevice();
LifeCycle lifeCycle = ( LifeCycle )getApplicationContext().getLifeCycleFactory().getLifeCycle();
lifeCycle.sleep();
// return true as we cannot reliably determinate what actually caused
// lifeCycle#sleep() to return
return true;
}
/**
* Notifies the client side to send a life cycle request as UI thread to
* perform UI-updates. Note that this method may be called from any thread.
*
* <p>Note that this only works as expected if the
* <code>{@link org.eclipse.rap.rwt.lifecycle.UICallBack UICallBack}</code>
* mechanism is activated.</p>
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
*/
public void wake() {
synchronized( deviceLock ) {
if( isDisposed() ) {
error( SWT.ERROR_DEVICE_DISPOSED );
}
if( thread != Thread.currentThread() ) {
UICallBack.runNonUIThreadWithFakeContext( this, new Runnable() {
public void run() {
synchronizer.asyncExec( new WakeRunnable() );
}
} );
}
}
}
protected void wakeThread() {
UICallBack.runNonUIThreadWithFakeContext( this, new Runnable() {
public void run() {
UICallBackManager.getInstance().wakeClient();
}
} );
}
Object getDeviceLock() {
return deviceLock;
}
//////////
// Redraw
void redrawControl( Control control, boolean redraw ) {
if( redraw ) {
if( redrawControls == null ) {
redrawControls = new LinkedList<Control>();
}
if( !redrawControls.contains( control ) ) {
redrawControls.add( control );
}
} else {
if( redrawControls != null ) {
redrawControls.remove( control );
}
}
}
boolean needsRedraw( Control control ) {
return redrawControls != null && redrawControls.contains( control );
}
private boolean executeNextRedraw() {
boolean result = false;
if( redrawControls != null ) {
Iterator iterator = redrawControls.iterator();
if( iterator.hasNext() ) {
Control control = ( Control )iterator.next();
WidgetUtil.getLCA( control ).doRedrawFake( control );
redrawControls.remove( control );
result = true;
}
}
return result;
}
//////////////////////
// Information methods
/**
* Returns the single instance of the system tray or null when there is no
* system tray available for the platform.
*
* @return the system tray or <code>null</code>
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
* @since 1.4
*/
public Tray getSystemTray() {
checkDevice();
return null;
}
/**
* Returns the single instance of the application menu bar or null
* when there is no application menu bar for the platform.
*
* @return the application menu bar or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.4
*/
public Menu getMenuBar() {
checkDevice();
return null;
}
/**
* Returns the single instance of the system taskBar or null
* when there is no system taskBar available for the platform.
*
* @return the system taskBar or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.4
*/
public TaskBar getSystemTaskBar() {
checkDevice();
return null;
}
/**
* Returns the single instance of the system-provided menu for the application.
* On platforms where no menu is provided for the application this method returns null.
*
* @return the system menu or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.4
*/
public Menu getSystemMenu() {
checkDevice();
return null;
}
/**
* Returns the matching standard color for the given
* constant, which should be one of the color constants
* specified in class <code>SWT</code>. Any value other
* than one of the SWT color constants which is passed
* in will result in the color black. This color should
* not be free'd because it was allocated by the system,
* not the application.
*
* @param id the color constant
* @return the matching color
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see SWT
*/
@Override
public Color getSystemColor( int id ) {
checkDevice();
Color result = null;
QxType value = null;
switch( id ) {
case SWT.COLOR_WIDGET_DARK_SHADOW:
value = ThemeUtil.getCssValue( "Display",
"rwt-darkshadow-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_WIDGET_NORMAL_SHADOW:
value = ThemeUtil.getCssValue( "Display",
"rwt-shadow-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_WIDGET_LIGHT_SHADOW:
value = ThemeUtil.getCssValue( "Display",
"rwt-lightshadow-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW:
value = ThemeUtil.getCssValue( "Display",
"rwt-highlight-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_WIDGET_BORDER:
value = ThemeUtil.getCssValue( "Display",
"rwt-thinborder-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_WIDGET_BACKGROUND:
// TODO [rst] Revise element name
value = ThemeUtil.getCssValue( "NONE",
"background-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_WIDGET_FOREGROUND:
// TODO [rst] Revise element name
value = ThemeUtil.getCssValue( "NONE", "color", SimpleSelector.DEFAULT );
break;
case SWT.COLOR_LIST_FOREGROUND:
value = ThemeUtil.getCssValue( "List", "color", SimpleSelector.DEFAULT );
break;
case SWT.COLOR_LIST_BACKGROUND:
value = ThemeUtil.getCssValue( "List",
"background-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_LIST_SELECTION:
value = ThemeUtil.getCssValue( "List-Item",
"background-color",
SimpleSelector.SELECTED );
break;
case SWT.COLOR_LIST_SELECTION_TEXT:
value = ThemeUtil.getCssValue( "List-Item",
"color",
SimpleSelector.SELECTED );
break;
case SWT.COLOR_INFO_FOREGROUND:
value = ThemeUtil.getCssValue( "Widget-ToolTip",
"color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_INFO_BACKGROUND:
value = ThemeUtil.getCssValue( "Display",
"rwt-infobackground-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_TITLE_FOREGROUND:
value = ThemeUtil.getCssValue( "Shell-Titlebar",
"color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_TITLE_INACTIVE_FOREGROUND:
value = ThemeUtil.getCssValue( "Shell-Titlebar",
"color",
SimpleSelector.INACTIVE );
break;
case SWT.COLOR_TITLE_BACKGROUND:
value = ThemeUtil.getCssValue( "Shell-Titlebar",
"background-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_TITLE_INACTIVE_BACKGROUND:
value = ThemeUtil.getCssValue( "Shell-Titlebar",
"background-color",
SimpleSelector.INACTIVE );
break;
case SWT.COLOR_TITLE_BACKGROUND_GRADIENT:
value = ThemeUtil.getCssValue( "Shell-Titlebar",
"background-gradient-color",
SimpleSelector.DEFAULT );
break;
case SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT:
value = ThemeUtil.getCssValue( "Shell-Titlebar",
"background-gradient-color",
SimpleSelector.INACTIVE );
break;
default:
result = super.getSystemColor( id );
}
if( value != null ) {
result = QxColor.createColor( ( QxColor )value );
if( result == null ) {
// TODO [rst] Revise: theming must prevent transparency for system colors
throw new IllegalArgumentException( "Transparent system color" );
}
}
return result;
}
/**
* Returns the matching standard platform image for the given
* constant, which should be one of the icon constants
* specified in class <code>SWT</code>. This image should
* not be free'd because it was allocated by the system,
* not the application. A value of <code>null</code> will
* be returned either if the supplied constant is not an
* SWT icon constant or if the platform does not define an
* image that corresponds to the constant.
*
* @param id the SWT icon constant
* @return the corresponding image or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see SWT#ICON_ERROR
* @see SWT#ICON_INFORMATION
* @see SWT#ICON_QUESTION
* @see SWT#ICON_WARNING
* @see SWT#ICON_WORKING
*/
public Image getSystemImage( int id ) {
checkDevice();
Image result = null;
switch( id ) {
case SWT.ICON_ERROR:
result = createSystemImage( "rwt-error-image" );
break;
case SWT.ICON_INFORMATION:
result = createSystemImage( "rwt-information-image" );
break;
case SWT.ICON_QUESTION:
result = createSystemImage( "rwt-question-image" );
break;
case SWT.ICON_WARNING:
result = createSystemImage( "rwt-warning-image" );
break;
case SWT.ICON_WORKING:
result = createSystemImage( "rwt-working-image" );
break;
}
return result;
}
private static Image createSystemImage( String cssProperty ) {
Image result = null;
QxType cssValue = ThemeUtil.getCssValue( "Display",
cssProperty,
SimpleSelector.DEFAULT );
if( cssValue != null ) {
try {
result = QxImage.createSwtImage( ( QxImage )cssValue );
} catch( IOException ioe ) {
throw new RuntimeException( "Could not read system image", ioe );
}
}
return result;
}
/**
* Returns the matching standard platform cursor for the given
* constant, which should be one of the cursor constants
* specified in class <code>SWT</code>. This cursor should
* not be free'd because it was allocated by the system,
* not the application. A value of <code>null</code> will
* be returned if the supplied constant is not an SWT cursor
* constant.
*
* @param id the SWT cursor constant
* @return the corresponding cursor or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see SWT#CURSOR_ARROW
* @see SWT#CURSOR_WAIT
* @see SWT#CURSOR_CROSS
* @see SWT#CURSOR_HELP
* @see SWT#CURSOR_SIZEALL
* @see SWT#CURSOR_SIZENS
* @see SWT#CURSOR_SIZEWE
* @see SWT#CURSOR_SIZEN
* @see SWT#CURSOR_SIZES
* @see SWT#CURSOR_SIZEE
* @see SWT#CURSOR_SIZEW
* @see SWT#CURSOR_SIZENE
* @see SWT#CURSOR_SIZESE
* @see SWT#CURSOR_SIZESW
* @see SWT#CURSOR_SIZENW
* @see SWT#CURSOR_IBEAM
* @see SWT#CURSOR_HAND
*
* @since 1.3
*/
public Cursor getSystemCursor( int id ) {
checkDevice();
return getApplicationContext().getResourceFactory().getCursor( id );
}
/**
* Returns the longest duration, in milliseconds, between
* two mouse button clicks that will be considered a
* <em>double click</em> <!-- by the underlying operating system -->.
*
* @return the double click time
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.2
*/
public int getDoubleClickTime() {
checkDevice();
return DOUBLE_CLICK_TIME;
}
/**
* Returns the control which the on-screen pointer is currently
* over top of, or null if it is not currently over one of the
* controls built by the currently running application.
*
* @return the control under the cursor or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public Control getCursorControl () {
checkDevice();
ControlFinder finder = new ControlFinder( this, getCursorLocation() );
return finder.getControl();
}
/**
* Returns the button dismissal alignment, one of <code>LEFT</code> or <code>RIGHT</code>.
* The button dismissal alignment is the ordering that should be used when positioning the
* default dismissal button for a dialog. For example, in a dialog that contains an OK and
* CANCEL button, on platforms where the button dismissal alignment is <code>LEFT</code>, the
* button ordering should be OK/CANCEL. When button dismissal alignment is <code>RIGHT</code>,
* the button ordering should be CANCEL/OK.
*
* @return the button dismissal order
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public int getDismissalAlignment() {
checkDevice();
return SWT.LEFT;
}
/**
* Returns true when the high contrast mode is enabled.
* Otherwise, false is returned.
* <p>
* Note: This operation is a hint and is not supported on
* platforms that do not have this concept.
* </p>
*
* @return the high contrast mode
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public boolean getHighContrast() {
checkDevice();
return false;
}
/**
* Returns the location of the on-screen pointer relative
* to the top left corner of the screen.
*
* @return the cursor location
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public Point getCursorLocation() {
checkDevice();
return new Point( cursorLocation.x, cursorLocation.y );
}
//////////
// Filters
/**
* Adds the listener to the collection of listeners who will be notified when
* an event of the given type occurs anywhere in a widget. The event type is
* one of the event constants defined in class <code>SWT</code>. When the
* event does occur, the listener is notified by sending it the
* <code>handleEvent()</code> message.
* <p>
* Setting the type of an event to <code>SWT.None</code> from within the
* <code>handleEvent()</code> method can be used to change the event type
* and stop subsequent Java listeners from running. Because event filters run
* before other listeners, event filters can both block other listeners and
* set arbitrary fields within an event. For this reason, event filters are
* both powerful and dangerous. They should generally be avoided for
* performance, debugging and code maintenance reasons.
* </p>
*
* @param eventType the type of event to listen for
* @param listener the listener which should be notified when the event occurs
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException
* <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been
* disposed</li>
* </ul>
* @see Listener
* @see SWT
* @see #removeFilter
* @see #removeListener
*/
public void addFilter( int eventType, Listener listener ) {
checkDevice();
if( listener == null ) {
error( SWT.ERROR_NULL_ARGUMENT );
}
if( filterTable == null ) {
filterTable = new EventTable();
}
filterTable.hook( eventType, listener );
}
/**
* Removes the listener from the collection of listeners who will be notified
* when an event of the given type occurs anywhere in a widget. The event type
* is one of the event constants defined in class <code>SWT</code>.
*
* @param eventType the type of event to listen for
* @param listener the listener which should no longer be notified when the
* event occurs
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException
* <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
* @see Listener
* @see SWT
* @see #addFilter
* @see #addListener
*/
public void removeFilter( int eventType, Listener listener ) {
checkDevice ();
if( listener == null ) {
error( SWT.ERROR_NULL_ARGUMENT );
}
if( filterTable == null ) {
return;
}
filterTable.unhook( eventType, listener );
if( filterTable.size() == 0 ) {
filterTable = null;
}
}
boolean filters( int eventType ) {
return filterTable != null ? filterTable.hooks( eventType ) : false;
}
void filterEvent( Event event ) {
if( filterTable != null ) {
filterTable.sendEvent( event );
}
}
void addLayoutDeferred( Composite comp ) {
if( layoutDeferred == null ) {
layoutDeferred = new Composite[ 64 ];
}
if( layoutDeferredCount == layoutDeferred.length ) {
Composite[] temp = new Composite[ layoutDeferred.length + 64 ];
System.arraycopy( layoutDeferred, 0, temp, 0, layoutDeferred.length );
layoutDeferred = temp;
}
layoutDeferred[ layoutDeferredCount++ ] = comp;
}
boolean runDeferredLayouts() {
boolean result = false;
if( layoutDeferredCount != 0 ) {
Composite[] temp = layoutDeferred;
int count = layoutDeferredCount;
layoutDeferred = null;
layoutDeferredCount = 0;
for( int i = 0; i < count; i++ ) {
Composite comp = temp[ i ];
if( !comp.isDisposed() ) {
comp.setLayoutDeferred( false );
}
}
result = true;
}
return result;
}
///////////////////
// Skinning support
void addSkinnableWidget( Widget widget ) {
if( skinList == null ) {
skinList = new Widget[ GROW_SIZE ];
}
if( skinCount >= skinList.length ) {
Widget[] newSkinWidgets = new Widget[ skinList.length + GROW_SIZE ];
System.arraycopy( skinList, 0, newSkinWidgets, 0, skinList.length );
skinList = newSkinWidgets;
}
skinList[ skinCount++ ] = widget;
}
boolean runSkin() {
boolean result = false;
if( skinCount > 0 ) {
Widget[] oldSkinWidgets = skinList;
int count = skinCount;
skinList = new Widget[ GROW_SIZE ];
skinCount = 0;
for( int i = 0; i < count; i++ ) {
Widget widget = oldSkinWidgets[ i ];
if( widget != null && !widget.isDisposed() ) {
widget.state &= ~Widget.SKIN_NEEDED;
oldSkinWidgets[ i ] = null;
sendSkinEvent( widget );
}
}
result = true;
}
return result;
}
private void sendSkinEvent( Widget widget ) {
Event event = new Event();
event.widget = widget;
sendEvent( SWT.Skin, event );
}
///////////////
// Data methods
/**
* Returns the application defined, display specific data
* associated with the receiver, or null if it has not been
* set. The <em>display specific data</em> is a single,
* unnamed field that is stored with every display.
* <p>
* Applications may put arbitrary objects in this field. If
* the object stored in the display specific data needs to
* be notified when the display is disposed of, it is the
* application's responsibility to provide a
* <code>disposeExec()</code> handler which does so.
* </p>
*
* @return the display specific data
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #setData(Object)
* @see #disposeExec(Runnable)
*
* @since 1.2
*/
public Object getData() {
checkDevice();
return data;
}
/**
* Sets the application defined, display specific data
* associated with the receiver, to the argument.
* The <em>display specific data</em> is a single,
* unnamed field that is stored with every display.
* <p>
* Applications may put arbitrary objects in this field. If
* the object stored in the display specific data needs to
* be notified when the display is disposed of, it is the
* application's responsibility provide a
* <code>disposeExec()</code> handler which does so.
* </p>
*
* @param data the new display specific data
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #getData()
* @see #disposeExec(Runnable)
*
* @since 1.2
*/
public void setData( Object data ) {
checkDevice();
this.data = data;
}
/**
* Sets the application defined property of the receiver
* with the specified name to the given argument.
* <p>
* Applications may have associated arbitrary objects with the
* receiver in this fashion. If the objects stored in the
* properties need to be notified when the display is disposed
* of, it is the application's responsibility provide a
* <code>disposeExec()</code> handler which does so.
* </p>
*
* @param key the name of the property
* @param value the new value for the property
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the key is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #getData(String)
* @see #disposeExec(Runnable)
*
* @since 1.2
*/
// [bm]: This is a verbatim copy of SWT, thus no reformatting was done.
public void setData( String key, Object value ) {
checkDevice ();
if (key == null) {
error (SWT.ERROR_NULL_ARGUMENT);
}
/* Remove the key/value pair */
if (value == null) {
if (keys == null) {
return;
}
int index = 0;
while (index < keys.length && !keys [index].equals (key)) {
index++;
}
if (index == keys.length) {
return;
}
if (keys.length == 1) {
keys = null;
values = null;
} else {
String [] newKeys = new String [keys.length - 1];
Object [] newValues = new Object [values.length - 1];
System.arraycopy (keys, 0, newKeys, 0, index);
System.arraycopy (keys, index + 1, newKeys, index, newKeys.length - index);
System.arraycopy (values, 0, newValues, 0, index);
System.arraycopy (values, index + 1, newValues, index, newValues.length - index);
keys = newKeys;
values = newValues;
}
return;
}
/* Add the key/value pair */
if (keys == null) {
keys = new String [] {key};
values = new Object [] {value};
return;
}
for (int i=0; i<keys.length; i++) {
if (keys [i].equals (key)) {
values [i] = value;
return;
}
}
String [] newKeys = new String [keys.length + 1];
Object [] newValues = new Object [values.length + 1];
System.arraycopy (keys, 0, newKeys, 0, keys.length);
System.arraycopy (values, 0, newValues, 0, values.length);
newKeys [keys.length] = key;
newValues [values.length] = value;
keys = newKeys;
values = newValues;
}
/**
* Returns the application defined property of the receiver
* with the specified name, or null if it has not been set.
* <p>
* Applications may have associated arbitrary objects with the
* receiver in this fashion. If the objects stored in the
* properties need to be notified when the display is disposed
* of, it is the application's responsibility to provide a
* <code>disposeExec()</code> handler which does so.
* </p>
*
* @param key the name of the property
* @return the value of the property or null if it has not been set
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the key is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #setData(String, Object)
* @see #disposeExec(Runnable)
*
* @since 1.2
*/
// [bm] This is a verbatim copy of SWT, thus no reformatting was done.
public Object getData( String key ) {
checkDevice ();
if( key == null ) {
error (SWT.ERROR_NULL_ARGUMENT);
}
if (keys == null) {
return null;
}
for (int i=0; i<keys.length; i++) {
if (keys [i].equals (key)) {
return values [i];
}
}
return null;
}
///////////
// Monitors
/**
* Returns an array of monitors attached to the device.
*
* @return the array of monitors
*
* @since 1.2
*/
public Monitor[] getMonitors() {
checkDevice();
if( monitor == null ) {
monitor = new Monitor( this );
}
return new Monitor[] { monitor };
}
/**
* Returns the primary monitor for that device.
*
* @return the primary monitor
*
* @since 1.2
*/
public Monitor getPrimaryMonitor() {
return getMonitors()[ 0 ];
}
////////////////////////
// AppName- and version
/**
* Returns the application name.
*
* @return the application name
*
* @see #setAppName(String)
*
* @since 1.3
*/
public static String getAppName() {
ISessionStore session = ContextProvider.getSessionStore();
return ( String )session.getAttribute( APP_NAME );
}
/**
* Returns the application version.
*
* @return the application version
*
* @see #setAppVersion(String)
*
* @since 1.3
*/
public static String getAppVersion() {
ISessionStore session = ContextProvider.getSessionStore();
return ( String )session.getAttribute( APP_VERSION );
}
/**
* Sets the application name to the argument.
* <p>
* Specifying <code>null</code> for the name clears it.
* </p>
*
* @param name the new app name or <code>null</code>
*
* @since 1.3
*/
public static void setAppName( String name ) {
ISessionStore session = ContextProvider.getSessionStore();
session.setAttribute( APP_NAME, name );
}
/**
* Sets the application version to the argument.
*
* @param version the new app version
*
* @since 1.3
*/
public static void setAppVersion( String version ) {
ISessionStore session = ContextProvider.getSessionStore();
session.setAttribute( APP_VERSION, version );
}
/**
* Forces all outstanding paint requests for the display
* to be processed before this method returns.
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see Control#update()
*
* @since 1.3
*/
public void update() {
checkDevice();
}
/**
* Causes the system hardware to emit a short sound
* (if it supports this capability).
*
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @since 1.3
*/
public void beep() {
checkDevice();
beep = true;
}
/**
* Returns true if a touch-aware input device is attached to the system,
* enabled, and ready for use.
*
* @since 1.4
*/
public boolean getTouchEnabled() {
checkDevice();
return false;
}
@SuppressWarnings("unchecked")
private void register() {
synchronized( Device.class ) {
boolean registered = false;
WeakReference[] displays = getDisplays();
for( int i = 0; !registered && i < displays.length; i++ ) {
if( canDisplayRefBeReplaced( displays[ i ] ) ) {
displays[ i ] = new WeakReference<Display>( this );
registered = true;
}
}
if( !registered ) {
WeakReference<Display>[] newDisplays = new WeakReference[ displays.length + 4 ];
System.arraycopy( displays, 0, newDisplays, 0, displays.length );
newDisplays[ displays.length ] = new WeakReference<Display>( this );
setDisplays( newDisplays );
}
}
}
private boolean canDisplayRefBeReplaced( WeakReference displayRef ) {
boolean result = false;
if( displayRef == null ) {
result = true;
} else {
Display display = ( Display )displayRef.get();
if( display == null || display.thread == thread ) {
result = true;
}
}
return result;
}
private void deregister() {
synchronized( Device.class ) {
WeakReference[] displays = getDisplays();
for( int i = 0; i < displays.length; i++ ) {
WeakReference current = displays[ i ];
if( current != null && this == current.get() ) {
displays[ i ] = null;
}
}
}
}
private static WeakReference<Display>[] getDisplays() {
return RWTFactory.getDisplaysHolder().getDisplays();
}
private void setDisplays( WeakReference<Display>[] displays ) {
getApplicationContext().getDisplaysHolder().setDisplays( displays );
}
/////////////////////
// Consistency checks
static boolean isValidClass( Class clazz ) {
// String name = clazz.getName();
// int index = name.lastIndexOf( '.' );
// return name.substring( 0, index + 1 ).equals( PACKAGE_PREFIX );
return true;
}
boolean isValidThread () {
return thread == Thread.currentThread ();
}
@Override
protected void checkDevice() {
if( !isValidThread() ) {
error( SWT.ERROR_THREAD_INVALID_ACCESS );
}
if( isDisposed() ) {
error( SWT.ERROR_DEVICE_DISPOSED );
}
}
//////////////////
// Helping methods
/**
* Does whatever display specific cleanup is required, and then uses the code
* in <code>SWTError.error</code> to handle the error.
*
* @param code the descriptive error code
* @see SWT#error(int)
*/
void error( int code ) {
SWT.error( code );
}
private Rectangle readInitialBounds() {
Rectangle result = ProtocolUtil.readPropertyValueAsRectangle( "w1", Display.BOUNDS );
if( result == null ) {
result = new Rectangle( 0, 0, 1024, 768 );
}
return result;
}
/////////////////
// Inner classes
private static class WakeRunnable implements Runnable, SerializableCompatibility {
public void run() {
}
}
private static final class ControlFinder {
private final Display display;
private final Point location;
private final Set<Control> foundComponentInParent;
private Control control;
ControlFinder( Display display, Point location ) {
this.display = display;
this.location = new Point( location.x, location.y );
foundComponentInParent = new HashSet<Control>();
find();
}
Control getControl() {
return control;
}
private void find() {
Shell[] shells = display.getShells();
for( int i = 0; control == null && i < shells.length; i++ ) {
WidgetTreeVisitor.accept( shells[ i ], new AllWidgetTreeVisitor() {
@Override
public boolean doVisit( Widget widget ) {
boolean result = true;
if( widget instanceof Control ) {
result = visitControl( ( Control )widget );
}
return result;
}
} );
}
}
private boolean visitControl( Control control ) {
Rectangle bounds = getAbsoluteBounds( control );
boolean result = false;
if( control.isVisible() && bounds.contains( location ) ) {
/*
* only assign control to cursor location if there was no other control
* already assigned within the same composite
*/
result = foundComponentInParent.add( control.getParent() );
if( result ) {
this.control = control;
}
}
return result;
}
private Rectangle getAbsoluteBounds( Control control ) {
Rectangle bounds = control.getBounds();
Point origin = getAbsoluteOrigin( control );
return new Rectangle( origin.x, origin.y, bounds.width, bounds.height );
}
}
private final class DisplayAdapter implements IDisplayAdapter {
public void setBounds( Rectangle bounds ) {
Display.this.bounds.x = bounds.x;
Display.this.bounds.y = bounds.y;
Display.this.bounds.width = bounds.width;
Display.this.bounds.height = bounds.height;
}
public void setCursorLocation( int x, int y ) {
cursorLocation.x = x;
cursorLocation.y = y;
}
public void setActiveShell( Shell activeShell ) {
Display.this.setActiveShell( activeShell );
}
public void setFocusControl( Control focusControl ) {
Display.this.setFocusControl( focusControl );
}
public void invalidateFocus() {
ContextProvider.getServiceStore().setAttribute( ATTR_INVALIDATE_FOCUS, Boolean.TRUE );
}
public boolean isFocusInvalidated() {
Object value = ContextProvider.getServiceStore().getAttribute( ATTR_INVALIDATE_FOCUS );
return value != null;
}
public Shell[] getShells() {
Shell[] result = new Shell[ shells.size() ];
shells.toArray( result );
return result;
}
public ISessionStore getSessionStore() {
return sessionStore;
}
public void attachThread() {
Display.this.attachThread();
}
public void detachThread() {
Display.this.detachThread();
}
public boolean isValidThread() {
return Display.this.isValidThread();
}
public boolean isBeepCalled() {
return beep;
}
public void resetBeep() {
beep = false;
}
}
}