/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.util;
import com.sun.lwuit.*;
import com.sun.lwuit.events.*;
import com.sun.lwuit.plaf.Style;
import java.util.Vector;
/**
* Handles event dispatching while guaranteeing that all events would
* be fired properly on the EDT regardless of their source. This class handles listener
* registration/removal in a safe and uniform way.
*
* @author Shai Almog
*/
public class EventDispatcher {
private Vector listeners;
private Object[] pending;
private Object pendingEvent;
private final Runnable callback = new Runnable() {
/**
* Do not invoke this method it handles the dispatching internally and serves
* as an implementation detail
*/
public final void run() {
if(!Display.getInstance().isEdt()) {
throw new IllegalStateException("This method should not be invoked by external code!");
}
if(pending instanceof ActionListener[]) {
fireActionSync((ActionListener[])pending, (ActionEvent)pendingEvent);
return;
}
if(pending instanceof FocusListener[]) {
fireFocusSync((FocusListener[])pending, (Component)pendingEvent);
return;
}
if(pending instanceof DataChangedListener[]) {
fireDataChangeSync((DataChangedListener[])pending, ((int[])pendingEvent)[0], ((int[])pendingEvent)[1]);
return;
}
if(pending instanceof SelectionListener[]) {
fireSelectionSync((SelectionListener[])pending, ((int[])pendingEvent)[0], ((int[])pendingEvent)[1]);
return;
}
if(pending instanceof StyleListener[]) {
Object[] p = (Object[])pendingEvent;
fireStyleChangeSync((StyleListener[])pending, (String)p[0], (Style)p[1]);
pendingEvent = null;
pending = null;
return;
}
}
};
/**
* Add a listener to the dispatcher that would receive the events when they occurs
*
* @param listener a dispatcher listener to add
*/
public synchronized void addListener(Object listener) {
if(listeners == null) {
listeners = new Vector();
}
listeners.addElement(listener);
}
/**
* Returns the vector of the listeners
*
* @return the vector of listeners attached to the event dispatcher
*/
public Vector getListenerVector() {
return listeners;
}
/**
* Remove the listener from the dispatcher
*
* @param listener a dispatcher listener to remove
*/
public synchronized void removeListener(Object listener) {
if(listeners != null) {
listeners.removeElement(listener);
}
}
/**
* Fires the event safely on the EDT without risk of concurrency errors
*
* @param ev the ActionEvent to fire to the listeners
*/
public void fireDataChangeEvent(int index, int type) {
if(listeners == null || listeners.size() == 0) {
return;
}
DataChangedListener[] array;
synchronized(this) {
array = new DataChangedListener[listeners.size()];
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter] = (DataChangedListener)listeners.elementAt(iter);
}
}
// if we already are on the EDT just fire the event
if(Display.getInstance().isEdt()) {
fireDataChangeSync(array, type, index);
} else {
pending = array;
pendingEvent = new int[] {type, index};
Display.getInstance().callSeriallyAndWait(callback);
pending = null;
pendingEvent = null;
}
}
/**
* Fires the style change even to the listeners
*
* @param property the property name for the event
* @param source the style firing the event
*/
public void fireStyleChangeEvent(String property, Style source) {
if(listeners == null || listeners.size() == 0) {
return;
}
StyleListener[] array;
synchronized(this) {
array = new StyleListener[listeners.size()];
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter] = (StyleListener)listeners.elementAt(iter);
}
}
// if we already are on the EDT just fire the event
if(Display.getInstance().isEdt()) {
fireStyleChangeSync(array, property, source);
} else {
pending = array;
pendingEvent = new Object[] {property, source};
Display.getInstance().callSerially(callback);
}
}
/**
* Synchronious internal call for common code
*/
private void fireDataChangeSync(DataChangedListener[] array, int type, int index) {
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter].dataChanged(type, index);
}
}
/**
* Synchronious internal call for common code
*/
private void fireStyleChangeSync(StyleListener[] array, String property, Style source) {
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter].styleChanged(property, source);
}
}
/**
* Synchronious internal call for common code
*/
private void fireSelectionSync(SelectionListener[] array, int oldSelection, int newSelection) {
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter].selectionChanged(oldSelection, newSelection);
}
}
/**
* Fires the event safely on the EDT without risk of concurrency errors
*
* @param ev the ActionEvent to fire to the listeners
*/
public void fireActionEvent(ActionEvent ev) {
if(listeners == null || listeners.size() == 0) {
return;
}
ActionListener[] array;
synchronized(this) {
array = new ActionListener[listeners.size()];
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter] = (ActionListener)listeners.elementAt(iter);
}
}
// if we already are on the EDT just fire the event
if(Display.getInstance().isEdt()) {
fireActionSync(array, ev);
} else {
pending = array;
pendingEvent = ev;
Display.getInstance().callSeriallyAndWait(callback);
pending = null;
pendingEvent = null;
}
}
/**
* Fires the event safely on the EDT without risk of concurrency errors
*
* @param oldSelection old selection
* @param newSelection new selection
*/
public void fireSelectionEvent(int oldSelection, int newSelection) {
if(listeners == null || listeners.size() == 0) {
return;
}
SelectionListener[] array;
synchronized(this) {
array = new SelectionListener[listeners.size()];
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter] = (SelectionListener)listeners.elementAt(iter);
}
}
// if we already are on the EDT just fire the event
if(Display.getInstance().isEdt()) {
fireSelectionSync(array, oldSelection, newSelection);
} else {
pending = array;
pendingEvent = new int[] {oldSelection, newSelection};
Display.getInstance().callSeriallyAndWait(callback);
pending = null;
pendingEvent = null;
}
}
/**
* Synchronious internal call for common code
*/
private void fireActionSync(ActionListener[] array, ActionEvent ev) {
for(int iter = 0 ; iter < array.length ; iter++) {
if(!ev.isConsumed()) {
array[iter].actionPerformed(ev);
}
}
}
/**
* Fires the event safely on the EDT without risk of concurrency errors
*
* @param c the Component that gets the focus event
*/
public void fireFocus(Component c) {
if(listeners == null || listeners.size() == 0) {
return;
}
FocusListener[] array;
synchronized(this) {
array = new FocusListener[listeners.size()];
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter] = (FocusListener)listeners.elementAt(iter);
}
}
// if we already are on the EDT just fire the event
if(Display.getInstance().isEdt()) {
fireFocusSync(array, c);
} else {
pending = array;
pendingEvent = c;
Display.getInstance().callSeriallyAndWait(callback);
pending = null;
pendingEvent = null;
}
}
/**
* Synchronious internal call for common code
*/
private void fireFocusSync(FocusListener[] array, Component c) {
if(c.hasFocus()) {
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter].focusGained(c);
}
} else {
for(int iter = 0 ; iter < array.length ; iter++) {
array[iter].focusLost(c);
}
}
}
}