Hi,
Here is teh code I made to get Floating Modal Components on a Frame,
It works quite well, I still have a problem when creating Modal
JInternalFrame.. they desapear when resised.
I hope to find some help...
AnyWay If you don't need resizing this code shows no other problems
(yet?)
Regards
Pierre-Mikael Legris
------------------ ModalJpanel.java -------------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.event.InternalFrameListener;
import javax.swing.event.MouseInputAdapter;
/**
* Copyright 2004 SimpleData Sarl http://simpledata.ch<BR>
* Provided "As This" Can be used or modified freely as long as a
* reference to it's initial author and to SimpleData is kept.<BR><BR>
*
* <HR>
* A Container that shows up on a Frame as Modal<BR>
* Was initially written to get JInternalFrame as modal
* but it finally shows a great way of having floating
* Modal Components<BR>
* Known BUG: doesn't grab all keyboard strokes
* (exemple ALT+... for menus)<BR>
* Known BUG2: When using JInternalFrames, resizes makes it disapear
* for a totally unkown reason.<BR>
* @author Pierre-Mikael Legris http://simpledata.ch
*/
public class ModalJPanel implements AWTEventListener {
// to avoid loops
private boolean closing = false;
// the Frame I belong To
private JFrame frame;
// the GlassPane the frame has before mine
private Component previousGlassPane;
// the Glass to do the trick
private JPanel glass;
// I don't not trust isVisible() call ;)
private boolean myVisible;
// The Container I'm displaying
private Container content;
/**
* Modal component that has the ability of
* locking the actual JFrame. <BR>
* If you extends this class or uses it without using
* a createXXXX method
* do not forget to create a way to call ModalJPanel.close()
* @param origin the origin component to display it
*/
public ModalJPanel(Container content,Component origin) {
this.content = content;
myVisible = false;
// get the first JFrame containing this Component
frame = (JFrame)
SwingUtilities.getAncestorOfClass(new Frame().getClass(), origin);
if (frame == null) {
System.out.println(
"Cannot find a JFrame for component:"+origin);
return;
}
// create opaque glass pane
glass = new JPanel();
glass.setLayout(null);
glass.setOpaque(false);
// Attach modal behavior to frame
// Associate dummy mouse listeners
// Otherwise mouse events pass through
MouseInputAdapter adapter = new MouseInputAdapter(){};
glass.addMouseListener(adapter);
glass.addMouseMotionListener(adapter);
glass.addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
}
});
// Add this modal internal frame to glass pane
glass.add(content);
glass.setCursor(Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR));
glass.setSize(frame.getSize());
content.setCursor(
Cursor.getPredefinedCursor (Cursor.DEFAULT_CURSOR));
}
/**
* close and release the glass Pane
*/
public void close() {
// Avoid infinite close loops
if (closing) return;
closing = true;
// unregister listeners
setVisible(false);
// release the glass
glass.removeAll();
glass = null;
// release my components
content.removeAll();
content = null;
}
/**
* Override setVisible to install/remove key events
* hook that will allow
* us to disable key events when the glass pane is visible.
*/
public void setVisible(boolean visible)
{
Toolkit tk = Toolkit.getDefaultToolkit();
// visible called and not visible yet
if (visible && (! myVisible))
{
myVisible = true;
// Grab key events
tk.addAWTEventListener (this, AWTEvent.KEY_EVENT_MASK);
// store previous Pane .. dunno if it has any effect
previousGlassPane = frame.getGlassPane();
// Change glass pane to our panel
frame.setGlassPane(glass);
glass.setVisible(true);
// refresh evreything!
glass.revalidate();
glass.requestFocus();
content.requestFocus();
frame.repaint();
}
// ! visible called and I'm visible
if ((! visible) && myVisible) {
myVisible = false;
// Release key events
tk.removeAWTEventListener (this);
glass.setVisible(false);
// put the GlassPane Back in order
frame.setGlassPane(previousGlassPane);
}
content.setVisible(visible);
}
/*
* To grab all key events
* @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent)
*/
public void eventDispatched(AWTEvent event) {
if (event instanceof KeyEvent &&
event.getSource() instanceof Component)
{
Component destination = (Component) event.getSource();
// Destination is me or one of my fellows Components :) nice..
if (SwingUtilities.isDescendingFrom(destination,content))
return;
JFrame master = (JFrame)
SwingUtilities.getAncestorOfClass(new Frame().getClass(),
destination);
// Consume events only for our window
if (master == this.frame)
((KeyEvent)event).consume();
}
}
/**
* Place smartly a component on a Container.
* by preventing it from going out of a Frame.<BR>
* Note: c.getSize() will be called and assumed as the final size
* as well as c.getParent().getSize()
* <B>Warning:</B> this method
* MUST BE CALLED <B>AFTER</B> the Component
* has been added!! and Container should have a null layout
* @param desiredLocation is the prefered Location on this container
* @param c the Component you want to display
*/
public static void smartLocation(Point desiredLocation, Component c)
{
Container master = c.getParent();
// find out my JFrame
if (master == null) return; // failed no Container
Dimension masterD = master.getSize();
Dimension cD = c.getSize();
Point result = (Point) desiredLocation.clone();
if ((result.getY() + cD.getHeight()) > masterD.getHeight())
result.setLocation(result.getX(),masterD.getHeight()-cD.getHeight());
if ((result.getX() + cD.getWidth()) > masterD.getWidth())
result.setLocation(masterD.getWidth()-cD.getWidth(),result.getY());
// allways prefers to left corner
if (result.getX() < 0) result.setLocation(0d,result.getY());
if (result.getY() < 0) result.setLocation(result.getX(),0d);
// done
c.setLocation(result);
}
/**
* Do the Math for originLocation + delta
*/
public static Point getDesiredLocation
(Component c,Component origin,Point delta) {
// set the location near it's origin
Point pointOrigin =
SwingUtilities.convertPoint(origin,0, 0, c.getParent());
Point desiredLocation = new Point();
desiredLocation.setLocation(pointOrigin.getX() + delta.getX(),
pointOrigin.getY() + delta.getY());
return desiredLocation;
}
//-------------------- CREATION ----------------------------//
/**
* Create a floating Modal JInternalFrame<BR>
* <B>Placement</B><BR>
* Assuming pointOrigin is the position of origin on the Frame:<BR>
* the component will be placed at <BR>
* (pointOrigin.x + delta.x, pointOrigin.y + delta y)<BR><BR>
* <B>BUG!!</B> If you set resizable to true!! the
* Jintenal Frame will disapear on resize!!
* @param component the component to draw
* @param origin the origin component to display it
* @param delta the delta to apply to origin position.
* @param resizable true if you want this frame to be resizable
*/
public static ModalJPanel createSimpleModalJInternalFrame
(Component component,Component origin,Point delta,boolean resizable)
{
// create a JInternalFrame
JInternalFrame jif = new JInternalFrame("",true,resizable);
// get a ModalJpane containing this JIF
final ModalJPanel result = new ModalJPanel(jif,origin);
jif.addInternalFrameListener(new InternalFrameListener() {
// add a listener to call ModalJpane.close();
public void internalFrameClosed(InternalFrameEvent e) {
result.close();
}
public void internalFrameActivated(InternalFrameEvent e) {}
public void internalFrameClosing(InternalFrameEvent e) {}
public void internalFrameDeactivated(InternalFrameEvent e) {}
public void internalFrameDeiconified(InternalFrameEvent e) {}
public void internalFrameIconified(InternalFrameEvent e) {}
public void internalFrameOpened(InternalFrameEvent e) {}
});
// add the component to the JInternalFrame
jif.getContentPane().add(component);
jif.setVisible(true);
// setThis item visible
result.setVisible(true);
// for a mysterious reason placement, position and
// getPrefferdSize() gives valid result
// only when result is Visible.. ???
jif.setSize(jif.getPreferredSize());
smartLocation(getDesiredLocation(jif,origin,delta),jif);
return result;
}
/**
* Create a floating JPanel with a close button on it
* Use this as an exemple If you want to use this class<BR><BR>
* <B>Placement</B><BR>
* Assuming pointOrigin is the position of origin on the Frame:<BR>
* the component will be placed at <BR>
* (pointOrigin.x + delta.x, pointOrigin.y + delta y)<BR><BR>
* @param component the component to draw
* @param origin the origin component to display it
* @param delta the delta to apply to origin position.
*/
public static ModalJPanel createSimpleModalJPanel
(Component component,Component origin,Point delta) {
Container c = new Container();
final ModalJPanel result = new ModalJPanel(c,origin);
// ----------- ok now design of our popup ------------//
c.setLayout(new BorderLayout());
// close button
JButton jb = new JButton("X");
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
result.close();
}
});
// add Components
c.add(jb,BorderLayout.NORTH);
c.add(component,BorderLayout.CENTER);
// debug COmponents
c.add(new TextField(),BorderLayout.SOUTH);
// setThis item visible
result.setVisible(true);
// for a mysterious reason placement, position and
// getPrefferdSize() gives valid result
// only when result is Visible.. ???
c.setSize(c.getPreferredSize());
smartLocation(getDesiredLocation(c,origin,delta),c);
return result;
}
}
Pierre-Mikael Legris - 01 Feb 2004 15:10 GMT
Posting to publication tooks enough time to find a work around.
The problem is that something is calling setVisible(false) on the
glass..
which is not very convenient..
Now my glass overwrites JPanel's setVisible method, but.. this is just
a trick. And I'm not satisfied with it.. If would appreciate comments
or tips to do this in a better way.
BTW, I encountered problems with a JComBox that was displaying on the
GlassPane wich does mean UNDER my Component.. wasn't convenient
neither..
I used setLightWeightPopupEnabled(false) on the JCombox .. but I have
a problem with too .. comments, tips, etc.. welcome too :)
The class is "about" to be ready and some people may find it usefull .
It's visible on this web page:
http://simpledata.ch/public/ModalJPanel.java.html
(this is a modified version of what I posted previously)
Regards
PM Legris