org.jdesktop.swingx.JXFrame Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id: JXFrame.java 4158 2012-02-03 18:29:40Z kschaefe $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* 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 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx;
import org.jdesktop.beans.JavaBean;
import org.jdesktop.swingx.util.WindowUtils;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.JToolBar;
import javax.swing.Timer;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.GraphicsConfiguration;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.Logger;
/**
*
* {@code JXFrame} is an enhanced {@link JFrame}. While {@code JXFrame} can
* replace any {@code JFrame}, it has features that make it particularly useful
* as the "main" frame for an application.
*
* Additional Features
*
* Root pane: {@code JXFrame} uses {@link JXRootPane} as its default root pane.
* The frame provide several convenience methods to provide easy access to the
* additional features.
*
*
* Idle: {@code JXFrame} offers an idle timer. Registering a
* {@link java.beans.PropertyChangeListener} for "idle" will notify when the
* user has not interacted with the JVM. A primary use for this type of
* functionality is to secure the application, blocking access and requiring the
* user to login again.
*
*
* Wait (busy) glass pane: The {@code JXFrame} can be configured with an
* alternate glass pane. Typically, this glass pane is used to notify the user
* that the application is busy, but the glass pane could be for any purpose.
* This secondary glass pane can be quickly enabled or disabled by
* {@linkplain #setWaitPaneVisible(boolean) setting the wait pane visible}.
*
*
* @author unascribed from JDNC
*/
@JavaBean
@SuppressWarnings("nls")
public class JXFrame extends JFrame {
protected static final Logger LOG = Logger.getLogger(JXFrame.class.getName());
/**
* An enumeration of {@link JXFrame} starting locations.
*
* @author unascribed from JDNC
*/
public enum StartPosition {CenterInScreen, CenterInParent, Manual}
private Component waitPane = null;
private Component glassPane = null;
private boolean waitPaneVisible = false;
private Cursor realCursor = null;
private boolean waitCursorVisible = false;
private boolean waiting = false;
private StartPosition startPosition;
private final boolean hasBeenVisible = false; //startPosition is only used the first time the window is shown
private final AWTEventListener keyEventListener; //for listening to KeyPreview events
private boolean keyPreview = false;
private final AWTEventListener idleListener; //for listening to events. If no events happen for a specific amount of time, mark as idle
private final Timer idleTimer;
private long idleThreshold = 0;
private boolean idle;
/**
* Creates a {@code JXFrame} with no title and standard closing behavior.
*/
public JXFrame() {
this(null, false);
}
/**
* Creates a {@code JXFrame} with the specified title and default closing
* behavior.
*
* @param title the frame title
*/
public JXFrame(String title) {
this(title, false);
}
/**
* Creates a JXFrame
in the specified
* GraphicsConfiguration
of
* a screen device, a blank title and default closing behaviour.
*
*
* @param gc the GraphicsConfiguration
that is used
* to construct the new Frame
;
* if gc
is null
, the system
* default GraphicsConfiguration
is assumed
* @throws IllegalArgumentException if gc
is not from
* a screen device. This exception is always thrown when
* GraphicsEnvironment.isHeadless() returns true.
*/
public JXFrame(GraphicsConfiguration gc) {
this(null, gc, false);
}
/**
* Creates a JXFrame
with the specified title, the
* specified GraphicsConfiguration
of a screen device and
* default closing behaviour.
*
*
* @param title the title to be displayed in the
* frame's border. A null
value is treated as
* an empty string, "".
* @param gc the GraphicsConfiguration
that is used
* to construct the new JFrame
with;
* if gc
is null
, the system
* default GraphicsConfiguration
is assumed
* @throws IllegalArgumentException if gc
is not from
* a screen device. This exception is always thrown when
* GraphicsEnvironment.isHeadless() returns true.
*/
public JXFrame(String title, GraphicsConfiguration gc) {
this(title, gc, false);
}
/**
* Creates a {@code JXFrame} with the specified title and closing behavior.
*
* @param title the frame title
* @param exitOnClose {@code true} to override the default ({@link JFrame}) closing
* behavior and use {@link JFrame#EXIT_ON_CLOSE EXIT_ON_CLOSE}
* instead; {@code false} to use the default behavior
*/
public JXFrame(String title, boolean exitOnClose) {
this(title, null, exitOnClose);
}
/**
* Creates a {@code JXFrame} with the specified title, GraphicsConfiguration
* and closing behavior.
*
* @param title the frame title
* @param gc the GraphicsConfiguration
of the target screen
* device. If gc
is null
, the system
* default GraphicsConfiguration
is assumed.
* @param exitOnClose {@code true} to override the default ({@link JFrame})
* closing behavior and use {@link JFrame#EXIT_ON_CLOSE
* EXIT_ON_CLOSE} instead; {@code false} to use the default behavior
* @throws IllegalArgumentException if gc
is not from a
* screen device.
*/
public JXFrame(String title, GraphicsConfiguration gc, boolean exitOnClose) {
super(title, gc);
if (exitOnClose) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//create the event handler for key preview functionality
keyEventListener = aWTEvent -> {
if (aWTEvent instanceof KeyEvent) {
KeyEvent evt = (KeyEvent) aWTEvent;
for (KeyListener kl : getKeyListeners()) {
int id = aWTEvent.getID();
switch (id) {
case KeyEvent.KEY_PRESSED:
kl.keyPressed(evt);
break;
case KeyEvent.KEY_RELEASED:
kl.keyReleased(evt);
break;
case KeyEvent.KEY_TYPED:
kl.keyTyped(evt);
break;
default:
LOG.warning("Unhandled Key ID: " + id);
break;
}
}
}
};
idleTimer = new Timer(100, actionEvent -> setIdle(true));
//create the event handler for key preview functionality
idleListener = aWTEvent -> {
//reset the timer
idleTimer.stop();
//if the user is idle, then change to not idle
if (isIdle()) {
setIdle(false);
}
//start the timer
idleTimer.restart();
};
}
/**
* Sets the cancel button property on the underlying {@code JXRootPane}.
*
* @param button the {@code JButton} which is to be the cancel button
* @see #getCancelButton()
* @see JXRootPane#setCancelButton(JButton)
*/
public void setCancelButton(JButton button) {
getRootPaneExt().setCancelButton(button);
}
/**
* Returns the value of the cancel button property from the underlying
* {@code JXRootPane}.
*
* @return the {@code JButton} which is the cancel button
* @see #setCancelButton(JButton)
* @see JXRootPane#getCancelButton()
*/
public JButton getCancelButton() {
return getRootPaneExt().getCancelButton();
}
/**
* Sets the default button property on the underlying {@code JRootPane}.
*
* @param button the {@code JButton} which is to be the default button
* @see #getDefaultButton()
* @see JXRootPane#setDefaultButton(JButton)
*/
public void setDefaultButton(JButton button) {
JButton old = getDefaultButton();
getRootPane().setDefaultButton(button);
firePropertyChange("defaultButton", old, getDefaultButton());
}
/**
* Returns the value of the default button property from the underlying
* {@code JRootPane}.
*
* @return the {@code JButton} which is the default button
* @see #setDefaultButton(JButton)
* @see JXRootPane#getDefaultButton()
*/
public JButton getDefaultButton() {
return getRootPane().getDefaultButton();
}
/**
* If enabled the {@code KeyListener}s will receive a preview of the {@code
* KeyEvent} prior to normal viewing.
*
* @param flag {@code true} to enable previewing; {@code false} otherwise
* @see #getKeyPreview()
* @see #addKeyListener(KeyListener)
*/
public void setKeyPreview(boolean flag) {
Toolkit.getDefaultToolkit().removeAWTEventListener(keyEventListener);
if (flag) {
Toolkit.getDefaultToolkit().addAWTEventListener(keyEventListener, AWTEvent.KEY_EVENT_MASK);
}
boolean old = keyPreview;
keyPreview = flag;
firePropertyChange("keyPreview", old, keyPreview);
}
/**
* Returns the value for the key preview.
*
* @return if {@code true} previewing is enabled; otherwise it is not
* @see #setKeyPreview(boolean)
*/
public final boolean getKeyPreview() {
return keyPreview;
}
/**
* Sets the start position for this frame. Setting this value only has an
* effect is the frame has never been displayed.
*
* @param position the position to display the frame at
* @see #getStartPosition()
* @see #setVisible(boolean)
*/
public void setStartPosition(StartPosition position) {
StartPosition old = getStartPosition();
this.startPosition = position;
firePropertyChange("startPosition", old, getStartPosition());
}
/**
* Returns the start position for this frame.
*
* @return the start position of the frame
* @see #setStartPosition(StartPosition)
*/
public StartPosition getStartPosition() {
return startPosition == null ? StartPosition.Manual : startPosition;
}
/**
* Switches the display cursor to or from the wait cursor.
*
* @param flag {@code true} to enable the wait cursor; {@code false} to
* enable the previous cursor
* @see #isWaitCursorVisible()
* @see Cursor#WAIT_CURSOR
*/
public void setWaitCursorVisible(boolean flag) {
boolean old = isWaitCursorVisible();
if (flag != old) {
waitCursorVisible = flag;
if (isWaitCursorVisible()) {
realCursor = getCursor();
super.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
} else {
super.setCursor(realCursor);
}
firePropertyChange("waitCursorVisible", old, isWaitCursorVisible());
}
}
/**
* Returns the state of the wait cursor visibility.
*
* @return {@code true} if the current cursor is the wait cursor; {@code
* false} otherwise
*/
public boolean isWaitCursorVisible() {
return waitCursorVisible;
}
/**
* {@inheritDoc}
*/
@Override
public void setCursor(Cursor c) {
if (!isWaitCursorVisible()) {
super.setCursor(c);
} else {
this.realCursor = c;
}
}
/**
* Sets the component to use as a wait glass pane. This component is not
* part of the display hierarchy unless {@code isWaitPaneVisible() == true}.
*
* @param c the wait glass pane for this frame
* @see #getWaitPane()
* @see #setWaitPaneVisible(boolean)
*/
public void setWaitPane(Component c) {
Component old = getWaitPane();
this.waitPane = c;
firePropertyChange("waitPane", old, getWaitPane());
}
/**
* Returns the current wait pane for this frame. This component may or may
* not be part of the display hierarchy.
*
* @return the current wait pane
* @see #setWaitPane(Component)
*/
public Component getWaitPane() {
return waitPane;
}
/**
* Enabled or disabled the display of the normal or wait glass pane. If
* {@code true} the wait pane is be displayed. Altering this property alters
* the display hierarchy.
*
* @param flag {@code true} to display the wait glass pane; {@code false} to
* display the normal glass pane
* @see #isWaitPaneVisible()
* @see #setWaitPane(Component)
*/
public void setWaitPaneVisible(boolean flag) {
boolean old = isWaitPaneVisible();
if (flag != old) {
this.waitPaneVisible = flag;
Component wp = getWaitPane();
if (isWaitPaneVisible()) {
glassPane = getRootPane().getGlassPane();
if (wp != null) {
getRootPane().setGlassPane(wp);
wp.setVisible(true);
}
} else {
if (wp != null) {
wp.setVisible(false);
}
getRootPane().setGlassPane(glassPane);
}
firePropertyChange("waitPaneVisible", old, isWaitPaneVisible());
}
}
/**
* Returns the current visibility of the wait glass pane.
*
* @return {@code true} if the wait glass pane is visible; {@code false}
* otherwise
*/
public boolean isWaitPaneVisible() {
return waitPaneVisible;
}
/**
* Sets the frame into a wait state or restores the frame from a wait state.
*
* @param waiting {@code true} to place the frame in a wait state; {@code false}
* otherwise
* @see #isWaiting()
* @see #setWaitCursorVisible(boolean)
* @see #setWaitPaneVisible(boolean)
*/
public void setWaiting(boolean waiting) {
boolean old = isWaiting();
this.waiting = waiting;
firePropertyChange("waiting", old, isWaiting());
setWaitPaneVisible(waiting);
setWaitCursorVisible(waiting);
}
/**
* Determines if the frame is in a wait state or not.
*
* @return {@code true} if the frame is in the wait state; {@code false}
* otherwise
* @see #setWaiting(boolean)
*/
public boolean isWaiting() {
return waiting;
}
/**
* {@inheritDoc}
*/
@Override
public void setVisible(boolean visible) {
if (!hasBeenVisible && visible) {
//move to the proper start position
StartPosition pos = getStartPosition();
switch (pos) {
case CenterInParent:
setLocationRelativeTo(getParent());
break;
case CenterInScreen:
setLocation(WindowUtils.getPointForCentering(this));
break;
case Manual:
default:
//nothing to do!
}
}
super.setVisible(visible);
}
public boolean isIdle() {
return idle;
}
/**
* Sets the frame into an idle state or restores the frame from an idle state.
*
* @param idle {@code true} to place the frame in an idle state; {@code false}
* otherwise
* @see #isIdle()
* @see #setIdleThreshold(long)
*/
public void setIdle(boolean idle) {
boolean old = isIdle();
this.idle = idle;
firePropertyChange("idle", old, isIdle());
}
/**
* Sets a threshold for user interaction before automatically placing the
* frame in an idle state.
*
* @param threshold the time (in milliseconds) to elapse before setting the frame
* idle
* @see #getIdleThreshold()
* @see #setIdle(boolean)
*/
public void setIdleThreshold(long threshold) {
long old = getIdleThreshold();
this.idleThreshold = threshold;
firePropertyChange("idleThreshold", old, getIdleThreshold());
threshold = getIdleThreshold(); // in case the getIdleThreshold method has been overridden
Toolkit.getDefaultToolkit().removeAWTEventListener(idleListener);
if (threshold > 0) {
Toolkit.getDefaultToolkit().addAWTEventListener(
idleListener,
AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK
);
}
idleTimer.stop();
idleTimer.setInitialDelay((int) threshold);
idleTimer.restart();
}
/**
* Returns the amount of time that must elapse before the frame
* automatically enters an idle state.
*
* @return the time in milliseconds
*/
public long getIdleThreshold() {
return idleThreshold;
}
/**
* Sets the status bar property on the underlying {@code JXRootPane}.
*
* @param statusBar the {@code JXStatusBar} which is to be the status bar
* @see #getStatusBar()
* @see JXRootPane#setStatusBar(JXStatusBar)
*/
public void setStatusBar(JXStatusBar statusBar) {
getRootPaneExt().setStatusBar(statusBar);
}
/**
* Returns the value of the status bar property from the underlying
* {@code JXRootPane}.
*
* @return the {@code JXStatusBar} which is the current status bar
* @see #setStatusBar(JXStatusBar)
* @see JXRootPane#getStatusBar()
*/
public JXStatusBar getStatusBar() {
return getRootPaneExt().getStatusBar();
}
/**
* Sets the tool bar property on the underlying {@code JXRootPane}.
*
* @param toolBar the {@code JToolBar} which is to be the tool bar
* @see #getToolBar()
* @see JXRootPane#setToolBar(JToolBar)
*/
public void setToolBar(JToolBar toolBar) {
getRootPaneExt().setToolBar(toolBar);
}
/**
* Returns the value of the tool bar property from the underlying
* {@code JXRootPane}.
*
* @return the {@code JToolBar} which is the current tool bar
* @see #setToolBar(JToolBar)
* @see JXRootPane#getToolBar()
*/
public JToolBar getToolBar() {
return getRootPaneExt().getToolBar();
}
//---------------------------------------------------- Root Pane Methods
/**
* Overridden to create a JXRootPane.
*/
@Override
protected JRootPane createRootPane() {
return new JXRootPane();
}
/**
* Overridden to make this public.
*/
@Override
public void setRootPane(JRootPane root) {
super.setRootPane(root);
}
/**
* Return the extended root pane. If this frame doesn't contain
* an extended root pane the root pane should be accessed with
* getRootPane().
*
* @return the extended root pane or null.
*/
public JXRootPane getRootPaneExt() {
if (rootPane instanceof JXRootPane) {
return (JXRootPane) rootPane;
}
return null;
}
}