package org.japura.gui.modal;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.RootPaneContainer;
import javax.swing.WindowConstants;
import javax.swing.event.InternalFrameListener;
/**
* Modal for frames and dialogs.
* <P>
* Adds a component as modal to the JFrame.
* <P>
* Each JFrame may have more than one component as modal, but only one component
* can be visible at once.
* <P>
* Copyright (C) 2009-2014 Carlos Eduardo Leite de Andrade
* <P>
* This library is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* <P>
* This program 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 Lesser General Public License for more
* details.
* <P>
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <A
* HREF="www.gnu.org/licenses/">www.gnu.org/licenses/</A>
* <P>
* For more information, contact: <A HREF="www.japura.org">www.japura.org</A>
* <P>
*
* @author Carlos Eduardo Leite de Andrade
*/
public class Modal{
private static Integer defaultModalDepth = Integer
.valueOf(JLayeredPane.DEFAULT_LAYER + 1);
private static Hashtable<RootPaneContainer, Modal> modals =
new Hashtable<RootPaneContainer, Modal>();
private RootPaneContainer rootPane;
private ModalPanel modalPanel;
private int oldDefaultCloseOperation;
private WindowListener[] oldWindowListeners;
private InternalFrameListener[] oldInternalFrameListeners;
private ComponentListener resizeListener;
private List<Component> components;
private HashMap<Component, List<ModalListener>> listeners;
private HashMap<Component, Integer> depths;
/**
* Get the current modal component of a specific frame.
*
* @param frame
* the frame with a modal
* @return Component
*/
public static Component getCurrentModal(JFrame frame) {
return getCurrentModal_(frame);
}
/**
* Get the current modal component of a specific dialog.
*
* @param dialog
* the dialog with a modal
* @return Component
*/
public static Component getCurrentModal(JDialog dialog) {
return getCurrentModal_(dialog);
}
/**
* Get the current modal component of a specific internal frame.
*
* @param frame
* the internal frame with a modal
* @return Component
*/
public static Component getCurrentModal(JInternalFrame frame) {
return getCurrentModal_(frame);
}
/**
* Get the current modal component of a specific RootPaneContainer.
*
* @param rootPane
* the RootPaneContainer with a modal
* @return Component or <code>NULL</code>
*/
private static Component getCurrentModal_(RootPaneContainer rootPane) {
synchronized (modals) {
Modal modal = modals.get(rootPane);
if (modal != null) {
if (modal.getSize() > 0) {
return modal.components.get(modal.components.size() - 1);
}
}
return null;
}
}
/**
* Close the current modal component of a specific frame.
*
* @param frame
* the frame with a modal
*/
public static void closeCurrentModal(JFrame frame) {
closeCurrentModal_(frame);
}
/**
* Close the current modal component of a specific dialog.
*
* @param dialog
* the dialog with a modal
*/
public static void closeCurrentModal(JDialog dialog) {
closeCurrentModal_(dialog);
}
/**
* Close the current modal component of a specific internal frame.
*
* @param frame
* the internal frame with a modal
*/
public static void closeCurrentModal(JInternalFrame frame) {
closeCurrentModal_(frame);
}
/**
* Close the current modal component of a specific RootPaneContainer.
*
* @param rootPane
* the RootPaneContainer with a modal
*/
private static void closeCurrentModal_(RootPaneContainer rootPane) {
synchronized (modals) {
Modal modal = modals.get(rootPane);
if (modal != null) {
modal.closeCurrent();
if (modal.getSize() == 0) {
modals.remove(rootPane);
}
}
}
}
/**
* Close a specific modal component.
*
* @param component
* the modal's component
*/
public static void closeModal(Component component) {
synchronized (modals) {
RootPaneContainer frame = null;
Modal modal = null;
Iterator<Entry<RootPaneContainer, Modal>> it =
modals.entrySet().iterator();
while (it.hasNext()) {
Entry<RootPaneContainer, Modal> entry = it.next();
modal = entry.getValue();
frame = entry.getKey();
if (frame != null && modal != null
&& modal.components.contains(component)) {
break;
} else {
frame = null;
modal = null;
}
}
if (frame != null && modal != null) {
modal.close(component);
if (modal.getSize() == 0) {
modals.remove(frame);
}
}
}
}
/**
* Close all modals of a specific frame.
*
* @param frame
* the frame with a modal
*/
public static void closeAllModals(JFrame frame) {
closeAllModals_(frame);
}
/**
* Close all modals of a specific dialog.
*
* @param dialog
* the dialog with a modal
*/
public static void closeAllModals(JDialog dialog) {
closeAllModals_(dialog);
}
/**
* Close all modals of a specific internal frame.
*
* @param frame
* the internal frame with a modal
*/
public static void closeAllModals(JInternalFrame frame) {
closeAllModals_(frame);
}
/**
* Close all modals of a specific RootPaneContainer.
*
* @param rootPane
* the RootPaneContainer with a modal
*/
private static void closeAllModals_(RootPaneContainer rootPane) {
synchronized (modals) {
Modal modal = modals.get(rootPane);
if (modal != null) {
modal.closeAll();
modals.remove(rootPane);
}
}
}
/**
* Indicates whether the frame has a modal.
*
* @param frame
* the frame with a modal
* @return boolean
*/
public static boolean hasModal(JFrame frame) {
return hasModal_(frame);
}
/**
* Indicates whether the dialog has a modal.
*
* @param dialog
* the dialog with a modal
* @return boolean
*/
public static boolean hasModal(JDialog dialog) {
return hasModal_(dialog);
}
/**
* Indicates whether the internal frame has a modal.
*
* @param frame
* the internal frame with a modal
* @return boolean
*/
public static boolean hasModal(JInternalFrame frame) {
return hasModal_(frame);
}
/**
* Indicates whether the RootPaneContainer has a modal.
*
* @param rootPane
* the RootPaneContainer with a modal
* @return boolean
*/
private static boolean hasModal_(RootPaneContainer rootPane) {
synchronized (modals) {
Modal modal = modals.get(rootPane);
if (modal != null && modal.getSize() > 0) {
return true;
}
return false;
}
}
/**
* Get the modal of a specific frame
*
* @param rootPane
* the RootPaneContainer with a modal
* @return {@link Modal}
*/
private static Modal getModal(RootPaneContainer rootPane) {
synchronized (modals) {
Modal modal = modals.get(rootPane);
if (modal == null) {
modal = new Modal(rootPane);
modals.put(rootPane, modal);
}
return modal;
}
}
/**
* Indicates whether the component is a modal component.
*
* @param component
* the component
* @return boolean
*/
public static boolean isModal(Component component) {
synchronized (modals) {
Iterator<Entry<RootPaneContainer, Modal>> it =
modals.entrySet().iterator();
while (it.hasNext()) {
Entry<RootPaneContainer, Modal> entry = it.next();
if (entry.getValue().components.contains(component)) {
return true;
}
}
return false;
}
}
/**
* Add a modal component in the internal frame.
*
* @param frame
* the internal frame
* @param component
* the component for modal
*/
public static void addModal(JInternalFrame frame, Component component) {
addModal(frame, component, null);
}
/**
* Add a modal component in the internal frame.
*
* @param frame
* the internal frame
* @param component
* the component for modal
* @param listener
* the listener for modal
*/
public static void addModal(JInternalFrame frame, Component component,
ModalListener listener) {
addModal_(frame, component, listener, defaultModalDepth);
}
/**
* Add a modal component in the internal frame.
*
* @param frame
* the internal frame
* @param component
* the component for modal
* @param listener
* the listener for modal
* @param modalDepth
* the depth for modal
*/
public static void addModal(JInternalFrame frame, Component component,
ModalListener listener, Integer modalDepth) {
addModal_(frame, component, listener, modalDepth);
}
/**
* Add a modal component in the dialog.
*
* @param dialog
* the dialog
* @param component
* the component for modal
*/
public static void addModal(JDialog dialog, Component component) {
addModal(dialog, component, null);
}
/**
* Add a modal component in the dialog.
*
* @param dialog
* the dialog
* @param component
* the component for modal
* @param listener
* the listener for modal
*/
public static void addModal(JDialog dialog, Component component,
ModalListener listener) {
addModal_(dialog, component, listener, defaultModalDepth);
}
/**
* Add a modal component in the dialog.
*
* @param dialog
* the dialog
* @param component
* the component for modal
* @param listener
* the listener for modal
* @param modalDepth
* the depth for modal
*/
public static void addModal(JDialog dialog, Component component,
ModalListener listener, Integer modalDepth) {
addModal_(dialog, component, listener, modalDepth);
}
public static void addModal(RootPaneContainer rootPane, Component component) {
addModal(rootPane, component, null);
}
public static void addModal(RootPaneContainer rootPane, Component component,
ModalListener listener) {
addModal(rootPane, component, listener, defaultModalDepth);
}
public static void addModal(RootPaneContainer rootPane, Component component,
ModalListener listener, Integer modalDepth) {
if (rootPane instanceof JFrame) {
Modal.addModal_((JFrame) rootPane, component, listener, modalDepth);
} else if (rootPane instanceof JDialog) {
Modal.addModal_((JDialog) rootPane, component, listener, modalDepth);
} else if (rootPane instanceof JInternalFrame) {
Modal.addModal_((JInternalFrame) rootPane, component, listener,
modalDepth);
} else {
return;
}
}
/**
* Add a modal component in the frame.
*
* @param frame
* the frame
* @param component
* the component for modal
*/
public static void addModal(JFrame frame, Component component) {
addModal(frame, component, null);
}
/**
* Add a modal component in the frame.
*
* @param frame
* the frame
* @param component
* the component for modal
* @param listener
* the listener for modal
*/
public static void addModal(JFrame frame, Component component,
ModalListener listener) {
addModal_(frame, component, listener, defaultModalDepth);
}
/**
* Add a modal component in the frame.
*
* @param frame
* the frame
* @param component
* the component for modal
* @param listener
* the listener for modal
* @param modalDepth
* the depth for modal
*/
public static void addModal(JFrame frame, Component component,
ModalListener listener, Integer modalDepth) {
addModal_(frame, component, listener, modalDepth);
}
/**
* Add a modal component in the RootPaneContainer.
*
* @param rootPane
* the RootPaneContainer
* @param component
* the component for modal
* @param listener
* the listener for modal
* @param modalDepth
* the depth for modal
*/
private static void addModal_(RootPaneContainer rootPane,
Component component, ModalListener listener,
Integer modalDepth) {
synchronized (modals) {
if (isModal(component) == false) {
getModal(rootPane).addModal(component, listener, modalDepth);
}
}
}
public static void addListener(JFrame frame, Component component,
ModalListener listener) {
addListener_(frame, component, listener);
}
public static void addListener(JDialog dialog, Component component,
ModalListener listener) {
addListener_(dialog, component, listener);
}
public static void addListener(JInternalFrame frame, Component component,
ModalListener listener) {
addListener_(frame, component, listener);
}
private static void addListener_(RootPaneContainer rootPane,
Component component, ModalListener listener) {
synchronized (modals) {
if (isModal(component)) {
getModal(rootPane).addListener(component, listener);
}
}
}
/**
* Get the modal count of a specific frame.
*
* @param frame
* the frame with a modal
* @return int
*/
public static int getModalCount(JFrame frame) {
return getModalCount_(frame);
}
/**
* Get the modal count of a specific dialog.
*
* @param dialog
* the dialog with a modal
* @return int
*/
public static int getModalCount(JDialog dialog) {
return getModalCount_(dialog);
}
/**
* Get the modal count of a specific internal frame.
*
* @param frame
* the internal frame with a modal
* @return int
*/
public static int getModalCount(JInternalFrame frame) {
return getModalCount_(frame);
}
/**
* Get the modal count of a specific RootPaneContainer.
*
* @param frame
* the RootPaneContainer with a modal
* @return int
*/
private static int getModalCount_(RootPaneContainer rootPane) {
synchronized (modals) {
Modal modal = modals.get(rootPane);
if (modal != null) {
return modal.getSize();
}
return 0;
}
}
/**
* Constructor
*
* @param rootPane
*/
private Modal(RootPaneContainer rootPane) {
this.rootPane = rootPane;
resizeListener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
resizeModalPanel();
}
};
components = new ArrayList<Component>();
depths = new HashMap<Component, Integer>();
listeners = new HashMap<Component, List<ModalListener>>();
this.rootPane.getRootPane().addComponentListener(resizeListener);
resizeModalPanel();
if (rootPane instanceof JFrame) {
backupProperties((JFrame) rootPane);
} else if (rootPane instanceof JDialog) {
backupProperties((JDialog) rootPane);
} else if (rootPane instanceof JInternalFrame) {
backupProperties((JInternalFrame) rootPane);
}
getModalPanel().setVisible(true);
}
private void backupProperties(JFrame frame) {
oldDefaultCloseOperation = frame.getDefaultCloseOperation();
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
oldWindowListeners = frame.getWindowListeners();
for (WindowListener listener : oldWindowListeners) {
frame.removeWindowListener(listener);
}
}
private void backupProperties(JDialog dialog) {
oldDefaultCloseOperation = dialog.getDefaultCloseOperation();
dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
oldWindowListeners = dialog.getWindowListeners();
for (WindowListener listener : oldWindowListeners) {
dialog.removeWindowListener(listener);
}
}
private void backupProperties(JInternalFrame frame) {
oldDefaultCloseOperation = frame.getDefaultCloseOperation();
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
oldInternalFrameListeners = frame.getInternalFrameListeners();
for (InternalFrameListener listener : oldInternalFrameListeners) {
frame.removeInternalFrameListener(listener);
}
}
/**
* Close the current modal.
*/
private void closeCurrent() {
if (components.size() > 0) {
Component component = components.remove(components.size() - 1);
getModalPanel().removeAll();
fireCloseActionListener(listeners.get(component));
listeners.remove(component);
if (components.size() > 0) {
showNextComponent();
} else {
restoreRootPane();
}
}
}
private void resizeModalPanel() {
Dimension dim = Modal.this.rootPane.getRootPane().getSize();
getModalPanel().setBounds(0, 0, dim.width, dim.height);
getModalPanel().revalidate();
}
private void restoreRootPane() {
rootPane.getRootPane().removeComponentListener(resizeListener);
rootPane.getLayeredPane().remove(getModalPanel());
if (rootPane instanceof JFrame) {
JFrame frame = (JFrame) rootPane;
frame.setDefaultCloseOperation(oldDefaultCloseOperation);
for (WindowListener listener : oldWindowListeners) {
frame.addWindowListener(listener);
}
} else if (rootPane instanceof JDialog) {
JDialog dialog = (JDialog) rootPane;
dialog.setDefaultCloseOperation(oldDefaultCloseOperation);
for (WindowListener listener : oldWindowListeners) {
dialog.addWindowListener(listener);
}
} else if (rootPane instanceof JInternalFrame) {
JInternalFrame frame = (JInternalFrame) rootPane;
frame.setDefaultCloseOperation(oldDefaultCloseOperation);
for (InternalFrameListener listener : oldInternalFrameListeners) {
frame.addInternalFrameListener(listener);
}
}
Component comp = (Component) rootPane;
comp.update(comp.getGraphics());
}
/**
* Close a modal
*
* @param component
* - the modal's component
*
*/
private void close(Component component) {
if (components.size() > 0) {
if (components.contains(component) == false) {
return;
}
components.remove(component);
depths.remove(component);
getModalPanel().removeAll();
fireCloseActionListener(listeners.get(component));
listeners.remove(component);
if (components.size() > 0) {
showNextComponent();
} else {
restoreRootPane();
}
}
}
private void showNextComponent() {
Component component = components.get(components.size() - 1);
Integer modalDepth = depths.get(component);
rootPane.getLayeredPane().remove(getModalPanel());
rootPane.getLayeredPane().add(getModalPanel(), modalDepth);
getModalPanel().add("", component);
getModalPanel().revalidate();
getModalPanel().repaint();
}
private void fireCloseActionListener(List<ModalListener> list) {
if (list != null) {
for (ModalListener listener : list) {
listener.modalClosed(new ModalEvent(rootPane));
}
}
}
/**
* Close all modals.
*/
private void closeAll() {
getModalPanel().removeAll();
components.clear();
depths.clear();
listeners.clear();
restoreRootPane();
}
/**
* Add a modal component
*
* @param component
* {@link Component}
* @param listener
* {@link ModalListener}
* @param modalDepth
*/
private void addModal(Component component, ModalListener listener,
Integer modalDepth) {
if (modalDepth == null) {
modalDepth = defaultModalDepth;
}
if (components.contains(component) == false) {
rootPane.getLayeredPane().remove(getModalPanel());
rootPane.getLayeredPane().add(getModalPanel(), modalDepth);
KeyboardFocusManager focusManager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.clearGlobalFocusOwner();
getModalPanel().removeAll();
getModalPanel().add("", component);
resizeModalPanel();
getModalPanel().repaint();
components.add(component);
depths.put(component, modalDepth);
addListener(component, listener);
}
}
private void addListener(Component component, ModalListener listener) {
if (components.contains(component)) {
List<ModalListener> list = listeners.get(component);
if (list == null) {
list = new ArrayList<ModalListener>();
listeners.put(component, list);
}
if (listener != null) {
list.add(listener);
}
}
}
/**
* Get the modal count
*
* @return int
*/
private int getSize() {
return components.size();
}
/**
* Get the transparent modal panel
*
* @return {@link ModalPanel}
*/
private ModalPanel getModalPanel() {
if (modalPanel == null) {
modalPanel = new ModalPanel();
modalPanel.setLayout(new ModalLayout());
}
return modalPanel;
}
/**
* Layout manager for modal panel
*
*/
private static class ModalLayout implements LayoutManager{
private Component comp;
@Override
public void addLayoutComponent(String name, Component comp) {
this.comp = comp;
}
@Override
public void layoutContainer(Container parent) {
if (comp != null) {
Dimension cdim = comp.getPreferredSize();
Dimension pdim = parent.getSize();
int x = (pdim.width / 2) - (cdim.width / 2);
int y = (pdim.height / 2) - (cdim.height / 2);
comp.setBounds(x, y, cdim.width, cdim.height);
}
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
Dimension dim = new Dimension();
if (comp != null) {
return comp.getPreferredSize();
}
return dim;
}
@Override
public void removeLayoutComponent(Component comp) {
}
}
/**
* Get the RootPaneContainer of the specific component.
*
* @param component
* @return the RootPaneContainer or <CODE>NULL</CODE> if its not exist.
*/
public static RootPaneContainer getRootPaneContainer(Component component) {
if (component instanceof RootPaneContainer) {
return (RootPaneContainer) component;
}
for (Container p = component.getParent(); p != null; p = p.getParent()) {
if (p instanceof RootPaneContainer) {
return (RootPaneContainer) p;
}
}
return null;
}
public static Component getCurrentModal(RootPaneContainer rootPane) {
if (rootPane != null) {
if (rootPane instanceof JFrame) {
return Modal.getCurrentModal((JFrame) rootPane);
} else if (rootPane instanceof JDialog) {
return Modal.getCurrentModal((JDialog) rootPane);
}
}
return null;
}
public static void closeCurrentModal(RootPaneContainer rootPane) {
if (rootPane != null) {
if (rootPane instanceof JFrame) {
Modal.closeCurrentModal((JFrame) rootPane);
} else if (rootPane instanceof JDialog) {
Modal.closeCurrentModal((JDialog) rootPane);
}
}
}
public static void closeAllModals(RootPaneContainer rootPane) {
if (rootPane != null) {
if (rootPane instanceof JFrame) {
JFrame frame = (JFrame) rootPane;
Modal.closeAllModals(frame);
} else if (rootPane instanceof JDialog) {
JDialog dialog = (JDialog) rootPane;
Modal.closeAllModals(dialog);
} else if (rootPane instanceof JInternalFrame) {
JInternalFrame frame = (JInternalFrame) rootPane;
Modal.closeAllModals(frame);
}
}
}
public static boolean hasModal(RootPaneContainer rootPane) {
if (rootPane == null) {
return false;
}
if (rootPane instanceof JFrame) {
return Modal.hasModal((JFrame) rootPane);
} else if (rootPane instanceof JDialog) {
return Modal.hasModal((JDialog) rootPane);
}
return false;
}
}