package DisplayProject;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import sun.awt.dnd.SunDropTargetEvent;
import Framework.LogMgr;
/**
* This glass pane component will stop all mouse events, except for a double click.
*
* If the first mouse click it receives is a double click, then it will pass that
* event on, as it means a single click has just occurred before it (the glass pane)
* was displayed. If it receives any other click events before a double click
* event, then it will not pass on the double click. Also, once it has received
* a double click event, it will not pass on any other double click events.
*
* This logic will be reset when the glass pane gets set to invisible/visible.
*
* This GlassPaneWithEvents component will be installed on the window when
* {@link UIutils#getGlassPane(Component)} is called.
*
* @author Craig Mitchell
* @since 31/03/2008
*/
@SuppressWarnings("serial")
public class GlassPaneWithEvents extends JComponent implements MouseListener {
/**
* A flag to indicate whether the glass pane is to be coloured
* to aid with the debugging effort. -DLogFlags="(trc:ui:2:255)"
*/
protected static boolean colouredGlassForDebug = LogMgr.getInstance().test(LogMgr.TYPE_DEBUG, LogMgr.SERVICE_UI, 2, 255);
/**
* Store the content pane we are blocking
*/
private Container contentPane;
/**
* This works out if a double click event will be allowed to be passed through
*/
private boolean doubleClickAllowed;
/**
* This is the current event we're processing. If no event is currently being processed then this will be
* null, however, this field can only be read by the EDT which will be waiting when not processing an event
* this should never be read as null.
*/
private static AWTEvent currentEvent = null;
static {
// DK:30/10/2008:intercept KeyEvents to consume all key actions once the GlassPane is visible.
//Allow one KeyRelese event once KeyPressed event was registered before the GlassPane become visible.
//KeyRelese event need to be allowed to complete Alt+Key actions as well as DefaultButton actions.
//In other case the action will be ignored due GlassPane is become visible in between Key Pressed and Released events.
// TF:20/11/2009:Forced this action to be done on the EDT
UIutils.invokeOnGuiThread(new Runnable() {
public void run() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new EventQueue() {
private Object lastPressedComponent = null;
@Override
protected void dispatchEvent(AWTEvent event) {
if (event instanceof KeyEvent && event.getSource() != null) {
Component glassPane = null;
if (event.getSource() instanceof JFrame) {
glassPane = ((JFrame) event.getSource()).getRootPane().getGlassPane();
} else if (event.getSource() instanceof JComponent) {
JFrame frame = UIutils.getWindowForComponent((JComponent)event.getSource());
if (frame != null) {
glassPane = frame.getRootPane().getGlassPane();
}
}
if (glassPane != null) {
if (glassPane.isVisible()) {
if (event.getID() == KeyEvent.KEY_RELEASED
&& lastPressedComponent == event.getSource()) {
//allow last pressed component to process release action
lastPressedComponent = null;
} else {
((KeyEvent) event).consume();
}
} else {
if (event.getID() == KeyEvent.KEY_PRESSED) {
//remember last pressed component
lastPressedComponent = event.getSource();
}
}
}
}
currentEvent = event;
try {
super.dispatchEvent(event);
//Bart Gauquie 2010/03/04: click mouse button and release mousebutton on a CompoundGraphic which is within a CompoundGraphic does not generate:
//java.awt.event.MouseEvent[MOUSE_PRESSED,(902,222),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on A3_MainHIC
//java.awt.event.MouseEvent[MOUSE_RELEASED,(902,222),button=1,modifiers=Button1,clickCount=1] on A3_MainHIC
//
//but
//
//java.awt.event.MouseEvent[MOUSE_PRESSED,(920,224),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on A3_MainHIC
//java.awt.event.InvocationEvent[INVOCATION_DEFAULT,runnable=sun.awt.dnd.SunDragSourceContextPeer$1@d8dcac,notifier=null,catchExceptions=false,when=1267698736927] on sun.awt.windows.WToolkit@330fb9
//sun.awt.dnd.SunDropTargetEvent[MOUSE_ENTERED,(920,224),button=0,clickCount=0] on A3_MainHIC
//sun.awt.PeerEvent[INVOCATION_DEFAULT,runnable=sun.awt.windows.WDropTargetContextPeer$1@1ffb136,notifier=null,catchExceptions=false,when=1267698736990] on A3_MainHIC
//sun.awt.PeerEvent[INVOCATION_DEFAULT,runnable=sun.awt.dnd.SunDragSourceContextPeer$EventDispatcher@1b23800,notifier=null,catchExceptions=false,when=1267698736990] on be.brail.displayproject.controls.TextGraphic[,1,1,34x10,alignmentX=0.0,alignmentY=0.0,border=,flags=58720256,maximumSize=,minimumSize=java.awt.Dimension[width=34,height=10],preferredSize=java.awt.Dimension[width=34,height=10],defaultIcon=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=EE47897,verticalAlignment=CENTER,verticalTextPosition=CENTER]
//sun.awt.dnd.SunDropTargetEvent[MOUSE_DROPPED,(920,224)] on A3_MainHIC
//
// so we create a new MouseReleased event with the same coordinates and dispatch that event also ...
if (event instanceof SunDropTargetEvent) {
SunDropTargetEvent sunDropTargetEvent = (SunDropTargetEvent) event;
if (sunDropTargetEvent.getID() == SunDropTargetEvent.MOUSE_DROPPED) {
MouseEvent mouseReleasedEvent = new MouseEvent((Component) event.getSource(), MouseEvent.MOUSE_RELEASED, sunDropTargetEvent.getWhen(),
MouseEvent.BUTTON1_MASK, sunDropTargetEvent.getX(), sunDropTargetEvent.getY(), 1, false, MouseEvent.BUTTON1);
super.dispatchEvent(mouseReleasedEvent);
}
}
}
finally {
currentEvent = null;
}
}
});
}
});
}
/**
* Return the current event. This method will return null unless the calling thread is the event dispatching
* thread, and the event dispatching thread is processing an event. (If the former condition is true, the
* latter condition is almost certain)
* @return
*/
public static AWTEvent getCurrentEvent() {
if (SwingUtilities.isEventDispatchThread()) {
return currentEvent;
}
else {
return null;
}
}
/**
* Main constructor.
*
* @param pContentPane The content pane that is directly below this glass pane.
*/
public GlassPaneWithEvents(Container pContentPane) {
this.addMouseListener(this);
this.contentPane = pContentPane;
this.doubleClickAllowed = true;
}
/**
* Take the mouse event and repost it on the appropriate widget.
*
* @param pEvent
* @param repaint
*/
private void redispatchMouseEvent(MouseEvent pEvent) {
// Get the mouse click point relative to the content pane
Point containerPoint = SwingUtilities.convertPoint(this, pEvent.getPoint(), this.contentPane);
// Find the component that is under this point
Component component = SwingUtilities.getDeepestComponentAt(
this.contentPane,
containerPoint.x,
containerPoint.y);
// Return if nothing was found
if (component == null) {
return;
}
// Convert point relative to the target component
Point componentPoint = SwingUtilities.convertPoint(
this,
pEvent.getPoint(),
component);
// Redispatch the event
component.dispatchEvent(new MouseEvent(component,
pEvent.getID(),
pEvent.getWhen(),
pEvent.getModifiers(),
componentPoint.x,
componentPoint.y,
pEvent.getClickCount(),
pEvent.isPopupTrigger()));
}
/**
* MouseClicked is the only event we care to redispatch.
*
* @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent e) {
// If we are a double click event, and we are allowed to happen. Redispatch the event.
if (e.getClickCount() == 2 && this.doubleClickAllowed) {
this.redispatchMouseEvent(e);
}
// Any thing other then a double click, and even after we have done a double click, we disable all further double click events.
this.doubleClickAllowed = false;
}
// Ignored events ...
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
// DK:02/03/2009: redispatch mouse Pressed and Released events either.
//because some of listeners are listening for mouse press/release events to process double click events
//If glass pane has been shown in between of two clicks then mouse press/release/click events with the second click
//will come to glass pane. That is why we need to redispatch them.
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2 && this.doubleClickAllowed) {
redispatchMouseEvent(e);
}
}
public void mouseReleased(MouseEvent e) {
if (e.getClickCount() == 2 && this.doubleClickAllowed) {
redispatchMouseEvent(e);
}
}
/**
* Reset our doubleClickAllowed flag.
*
* @see javax.swing.JComponent#setVisible(boolean)
*/
@Override
public void setVisible(boolean flag) {
super.setVisible(flag);
// We we are set to displayed, then allow double click events
this.doubleClickAllowed = true;
}
@Override
public void paint(Graphics g) {
if (colouredGlassForDebug) {
g.setColor(Color.cyan);
g.fillRect(0, 0, g.getClipBounds().width, g.getClipBounds().height);
g.setColor(Color.black);
int x = Math.max(g.getClipBounds().width, 1) / 2 - 20;
int y = Math.max(g.getClipBounds().height, 1) / 2 - 7;
g.drawString("LOCKED", x, y);
}
}
}