package com.osm.gui;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
/**
* An extended <code>JInternalFrame</code> that provides modality in a child/parent
* hierarchy
*
* @author webbyit
*/
public class JChildInternalFrame extends JInternalFrame {
public enum ChildFramePosition {
NORMAL,
RELATIVE_TO_PARENT,
RELATIVE_TO_DESKTOPPANE,
CENTER_TO_PARENT,
CENTER_TO_DESKTOPPANE
}
protected ChildFramePosition positionRelativeTo = ChildFramePosition.CENTER_TO_PARENT;
protected JDesktopPane desktopPane;
protected JComponent parent;
protected JChildInternalFrame childFrame;
protected JComponent focusOwner;
public boolean wasCloseable;
public boolean wasResizable;
public boolean wasMaximizable;
public boolean wasIconifiable;
public JChildInternalFrame() {
super();
calculateBounds();
}
public JChildInternalFrame(JComponent parent) {
super();
setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
setParentFrame(parent);
setFocusTraversalKeysEnabled(false);
if (parent != null && parent instanceof JChildInternalFrame) {
((JChildInternalFrame) parent).setChildFrame(JChildInternalFrame.this);
}
// Add glass pane
ModalityInternalGlassPane glassPane = new ModalityInternalGlassPane(this);
setGlassPane(glassPane);
addFrameListener(); // Add frame listeners
addFrameVetoListener(); // Add frame veto listenr
init(); // calculate size and position
}
public void appendToDesktopPane(JDesktopPane desktopPane) {
try {
desktopPane.add(this);
this.setSelected(true);
} catch (PropertyVetoException ex) {
Logger.getLogger(this.getName()).log(Level.SEVERE, null, ex);
}
}
public void setChildFramePosition(ChildFramePosition position) {
positionRelativeTo = position;
}
public ChildFramePosition getChildFramePosition() {
return positionRelativeTo;
}
private void setParentFrame(JComponent parent) {
desktopPane = JOptionPane.getDesktopPaneForComponent(parent);
this.parent = (parent == null) ? JOptionPane.getDesktopPaneForComponent(parent) : parent; // default to desktop if no parent given
}
public JComponent getParentFrame() {
return parent;
}
public void setChildFrame(JChildInternalFrame childFrame) {
this.childFrame = childFrame;
}
public JChildInternalFrame getChildFrame() {
return childFrame;
}
public boolean hasChildFrame() {
return (childFrame != null);
}
protected void addFrameVetoListener() {
addVetoableChangeListener(new VetoableChangeListener() {
@Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)
&& evt.getNewValue().equals(Boolean.TRUE)) {
if (hasChildFrame()) {
childFrame.setSelected(true);
if (childFrame.isIcon()) {
childFrame.setIcon(false);
}
throw new PropertyVetoException("no!", evt);
}
}
}
});
}
/**
* Method to control the display of the glasspane, dependant
* on the frame being active or not
*/
protected void addFrameListener() {
addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameActivated(InternalFrameEvent e) {
if (hasChildFrame() == true) {
getGlassPane().setVisible(true);
grabFocus();
} else {
getGlassPane().setVisible(false);
}
}
@Override
public void internalFrameDeactivated(InternalFrameEvent e) {
if (hasChildFrame() == true) {
getGlassPane().setVisible(true);
grabFocus();
} else {
getGlassPane().setVisible(false);
}
}
@Override
public void internalFrameOpened(InternalFrameEvent e) {
getGlassPane().setVisible(false);
}
@Override
public void internalFrameClosing(InternalFrameEvent e) {
if (parent != null && parent instanceof JChildInternalFrame) {
JChildInternalFrame parentFrame = (JChildInternalFrame) parent;
parentFrame.childClosing();
}
}
});
}
/**
* Method to handle child frame closing and make this frame
* available for user input again with no glasspane visible
*/
protected void childClosing() {
getGlassPane().setVisible(false);
if (focusOwner != null) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
moveToFront();
setSelected(true);
focusOwner.grabFocus();
} catch (PropertyVetoException ex) {
}
}
});
focusOwner.grabFocus();
}
getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
setChildFrame(null);
getDesktopPane().setSelectedFrame(this);
}
/*
* Method to handle child opening and becoming visible.
*/
protected void childOpening() {
// record the present focused component
focusOwner = (JComponent) getFocusOwner();
grabFocus();
getGlassPane().setVisible(true);
getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
@Override
public void show() {
if (parent != null && parent instanceof JChildInternalFrame) {
JChildInternalFrame parentFrame = (JChildInternalFrame) parent;
parentFrame.wasCloseable = parentFrame.closable;
parentFrame.wasResizable = parentFrame.resizable;
parentFrame.wasMaximizable = parentFrame.maximizable;
parentFrame.wasIconifiable = parentFrame.iconable;
// Need to inform parent its about to lose its focus due
// to child opening
parentFrame.childOpening();
parentFrame.setClosable(false);
parentFrame.setResizable(false);
parentFrame.setIconifiable(false);
parentFrame.setMaximizable(false);
}
calculateBounds();
super.show();
}
public void close() {
try {
if (parent != null && parent instanceof JChildInternalFrame) {
JChildInternalFrame parentFrame = (JChildInternalFrame) parent;
parentFrame.setClosable(parentFrame.wasCloseable);
parentFrame.setResizable(parentFrame.wasResizable);
parentFrame.setIconifiable(parentFrame.wasIconifiable);
parentFrame.setMaximizable(parentFrame.wasMaximizable);
}
setClosed(true);
} catch(PropertyVetoException e) {
}
}
protected void init() {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 394, Short.MAX_VALUE));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 274, Short.MAX_VALUE));
pack();
}
public void calculateBounds() {
Dimension frameSize = getPreferredSize();
Dimension parentSize = new Dimension();
Dimension rootSize = new Dimension(); // size of desktop
Point frameCoord = new Point();
if (desktopPane != null) {
rootSize = desktopPane.getSize(); // size of desktop
frameCoord = SwingUtilities.convertPoint(parent, 0, 0, desktopPane);
parentSize = parent.getSize();
}
//setBounds((rootSize.width - frameSize.width) / 2, (rootSize.height - frameSize.height) / 2, frameSize.width, frameSize.height);
int x = 0;
int y = 0;
// We want dialog centered relative to its parent component
switch(positionRelativeTo) {
case RELATIVE_TO_PARENT:
break;
case CENTER_TO_PARENT:
x = (parentSize.width - frameSize.width) / 2 + frameCoord.x;
y = (parentSize.height - frameSize.height) / 2 + frameCoord.y;
// If possible, dialog should be fully visible
int ovrx = x + frameSize.width - rootSize.width;
int ovry = y + frameSize.height - rootSize.height;
x = Math.max((ovrx > 0 ? x - ovrx : x), 0);
y = Math.max((ovry > 0 ? y - ovry : y), 0);
break;
case RELATIVE_TO_DESKTOPPANE:
// TODO: implementar comportamiento
break;
case NORMAL:
break;
}
setBounds(x, y, frameSize.width, frameSize.height);
}
/**
* Glass pane to overlay. Listens for mouse clicks and sets selected
* on associated modal frame. Also if modal frame has no children make
* class pane invisible
*/
class ModalityInternalGlassPane extends JComponent {
private JChildInternalFrame modalFrame;
public ModalityInternalGlassPane(JChildInternalFrame frame) {
modalFrame = frame;
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (modalFrame.isSelected() == false) {
try {
modalFrame.setSelected(true);
if (modalFrame.hasChildFrame() == false) {
setVisible(false);
}
} catch (PropertyVetoException e1) {
//e1.printStackTrace();
}
}
}
});
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(new Color(255, 255, 255, 100));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
}