/* * @(#)Dialog.java 1.19 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.awt; import java.awt.event.*; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import sun.awt.PeerBasedToolkit; import sun.awt.peer.DialogPeer; import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.PeerEvent; /** * A Dialog is a top-level window with a title and a border * that is typically used to take some form of input from the user. * * The size of the dialog includes any area designated for the * border. The dimensions of the border area can be obtained * using the <code>getInsets</code> method, however, since * these dimensions are platform-dependent, a valid insets * value cannot be obtained until the dialog is made displayable * by either calling <code>pack</code> or <code>show</code>. * Since the border area is included in the overall size of the * dialog, the border effectively obscures a portion of the dialog, * constraining the area available for rendering and/or displaying * subcomponents to the rectangle which has an upper-left corner * location of <code>(insets.left, insets.top)</code>, and has a size of * <code>width - (insets.left + insets.right)</code> by * <code>height - (insets.top + insets.bottom)</code>. * <p> * The default layout for a dialog is BorderLayout. * <p> * A dialog must have either a frame or another dialog defined as its * owner when it's constructed. When the owner window of a visible dialog * is hidden or minimized, the dialog will automatically be hidden * from the user. When the owner window is subsequently re-opened, then * the dialog is made visible to the user again. * <p> * A dialog can be either modeless (the default) or modal. A modal * dialog is one which blocks input to all other toplevel windows * in the app context, except for any windows created with the dialog * as their owner. * <p> * Dialogs are capable of generating the following window events: * WindowOpened, WindowClosing, WindowClosed, WindowActivated, * WindowDeactivated. * * <p> * <a name="restrictions"> * <h4>Restrictions</h4> * <em> * Implementations of Dialog in Personal Profile exhibit * certain restrictions, specifically: * <ul> * <li> An implementation may limit possible Dialog sizes. In such * a case, attempts to change the size of a Dialog may result in * a different size than what was requested. * See: * <ul> * <li> {@link #setSize(int, int)} * <li> {@link #setSize(Dimension)} * <li> {@link #setBounds(int, int, int, int)} * <li> {@link #setBounds(Rectangle)} * </ul> * <li> An implementation may prohibit resizing of Dialogs by a user. In * such a case, attempts to make any Dialog resizable will fail silently. * See: * <ul> * <li> {@link #setResizable(boolean)} * </ul> * <li> An implementation may limit Dialog locations. In * such a case, attempts to change the location of a Dialog may * result in a different location than what was requested. * See: * <ul> * <li> {@link #setLocation(int, int)} * <li> {@link #setLocation(Point)} * <li> {@link #setBounds(int, int, int, int)} * <li> {@link #setBounds(Rectangle)} * </ul> * <li> An implementation need not support a visible title on Dialogs. * In such a case, the methods {@link #setTitle} and {@link #getTitle} * on all Dialogs behave as normal, but no title is visible to the user. * See: * <ul> * <li> {@link #Dialog(Frame, String)} * <li> {@link #Dialog(Frame, String, boolean)} * <li> {@link #setTitle} * </ul> * </ul> * </em> * * @see WindowEvent * @see Window#addWindowListener * * @version 1.8, 07/03/02 * @author Sami Shaio * @author Arthur van Hoff * @since JDK1.0 */ public class Dialog extends Window { /** * A dialog's resizable property. Will be true * if the Dialog is to be resizable, otherwise * it will be false. * * @serial * @see setResizable() */ boolean resizable = true; /** * This field indicates whether the dialog is undecorated. * This property can only be changed while the dialog is not displayable. * <code>undecorated</code> will be true if the dialog is * undecorated, otherwise it will be false. * * @serial * @see #setUndecorated(boolean) * @see #isUndecorated() * @see Component#isDisplayable() * @since 1.4 */ boolean undecorated = false; /** * Will be true if the Dialog is modal, * otherwise the dialog will be modeless. * A modal Dialog grabs all the input to * the owner frame from the user. * * @serial * @see isModal() * @see setModal() */ boolean modal; /** * Specifies the title of the Dialog. * This field can be null. * * @serial * @see getTitle() * @see setTitle() */ String title; private transient boolean keepBlocking = false; private static final String base = "dialog"; private static int nameCounter = 0; /* * JDK 1.1 serialVersionUID */ private static final long serialVersionUID = 5920926903803293709L; /** * Constructs an initially invisible, non-modal Dialog with * an empty title and the specified owner frame. * @param owner the owner of the dialog * @exception java.lang.IllegalArgumentException if <code>owner</code> * is <code>null</code> * @see Component#setSize * @see Component#setVisible */ public Dialog(Frame owner) { this(owner, "", false); } /** * Constructs an initially invisible Dialog with an empty title, * the specified owner frame and modality. * @param owner the owner of the dialog * @param modal if true, dialog blocks input to other app windows when shown * @exception java.lang.IllegalArgumentException if <code>owner</code> * is <code>null</code> */ public Dialog(Frame owner, boolean modal) { this(owner, "", modal); } /** * Constructs an initially invisible, non-modal Dialog with * the specified owner frame and title. * <p> * <em>Note: This operation is subject to * <a href="#restrictions">restriction</a> * in Personal Profile.</em> * * @param owner the owner of the dialog * @param title the title of the dialog. A <code>null</code> value * will be accepted without causing a NullPointerException * to be thrown. * @exception java.lang.IllegalArgumentException if <code>owner</code> * is <code>null</code> * @see Component#setSize * @see Component#setVisible */ public Dialog(Frame owner, String title) { this(owner, title, false); } /** * Constructs an initially invisible Dialog with the * specified owner frame, title, and modality. * <p> * <em>Note: This operation is subject to * <a href="#restrictions">restriction</a> * in Personal Profile.</em> * * @param owner the owner of the dialog * @param title the title of the dialog. A <code>null</code> value * will be accepted without causing a NullPointerException * to be thrown. * @param modal if true, dialog blocks input to other app windows when shown * @exception java.lang.IllegalArgumentException if <code>owner</code> * is <code>null</code> * @see Component#setSize * @see Component#setVisible */ public Dialog(Frame owner, String title, boolean modal) { super(owner); if (owner == null) { throw new IllegalArgumentException("null owner frame"); } this.title = title; setModal(modal); } /** * Constructs an initially invisible Dialog with the * specified owner frame, title, modality, and * <code>GraphicsConfiguration</code>. * @param owner the owner of the dialog * @param title the title of the dialog. A <code>null</code> value * will be accepted without causing a NullPointerException * to be thrown. * @param modal if true, dialog blocks input to other app windows when shown * @param gc the <code>GraphicsConfiguration</code> * of the target screen device. If <code>gc</code> is * <code>null</code>, the same * <code>GraphicsConfiguration</code> as the owning Frame is used. * @exception java.lang.IllegalArgumentException if <code>owner</code> * is <code>null</code>. This exception is always thrown * when GraphicsEnvironment.isHeadless() returns true * @see java.awt.GraphicsEnvironment#isHeadless * @see Component#setSize * @see Component#setVisible * @since 1.4 */ public Dialog(Frame owner, String title, boolean modal, GraphicsConfiguration gc) { super(owner, gc); this.title = title; this.modal = modal; setFocusTraversalPolicy(KeyboardFocusManager. getCurrentKeyboardFocusManager(). getDefaultFocusTraversalPolicy() ); } /** * Construct a name for this component. Called by getName() when the * name is null. */ String constructComponentName() { return base + nameCounter++; } /** * Creates the dialog's peer. The peer allows us to change the appearance * of the frame without changing its functionality. * @since JDK1.0 */ public void addNotify() { synchronized (getTreeLock()) { // Bug Fix : 5012247 // As per the spec, if the "owner" of the Dialog is not // displayable when Dialog.show() is called, then it should // be made displayable. // (Note : displayable state means that native resources // are assigned to the component) if (parent != null && parent.peer == null) { //(!displayable()) parent.addNotify(); } // Bug Fix : 5012247 if (peer == null) { peer = ((PeerBasedToolkit) getToolkit()).createDialog(this); } super.addNotify(); } } /** * Indicates whether the dialog is modal. * When a modal Dialog is made visible, user input will be * blocked to the other windows in the app context, except for * any windows created with this dialog as their owner. * * @return <code>true</code> if this dialog window is modal; * <code>false</code> otherwise. * @see java.awt.Dialog#setModal */ public boolean isModal() { return modal; } /** * Specifies whether this dialog should be modal. * @see java.awt.Dialog#isModal * @since JDK1.1 */ public void setModal(boolean modal) { this.modal = modal; } /** * Gets the title of the dialog. The title is displayed in the * dialog's border. * @return the title of this dialog window. The title may be * <code>null</code>. * @see java.awt.Dialog#setTitle */ public String getTitle() { return title; } /** * Sets the title of the Dialog. * <p> * <em>Note: This operation is subject to * <a href="#restrictions">restriction</a> * in Personal Profile.</em> * * @param title the title displayed in the dialog's border * @see #getTitle */ public synchronized void setTitle(String title) { this.title = title; DialogPeer peer = (DialogPeer) this.peer; if (peer != null) { peer.setTitle(title); } } /** * @return true if we actually showed, false if we just called toFront() */ private boolean conditionalShow() { boolean retval; synchronized (getTreeLock()) { if (peer == null) { addNotify(); } validate(); if (visible) { toFront(); retval = false; } else { visible = retval = true; peer.setVisible(true); // now guaranteed never to block } if (retval && (componentListener != null || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0)) { ComponentEvent e = new ComponentEvent(this, ComponentEvent.COMPONENT_SHOWN); Toolkit.getEventQueue().postEvent(e); } } if (retval && (state & OPENED) == 0) { postWindowEvent(WindowEvent.WINDOW_OPENED); state |= OPENED; } return retval; } // LMK app context support /** * Stores the app context on which event dispatch thread the dialog * is being shown. Initialized in show(), used in hideAndDisposeHandler() */ transient private AppContext showAppContext; /** * Makes the Dialog visible. If the dialog and/or its owner * are not yet displayable, both are made displayable. The * dialog will be validated prior to being made visible. * If the dialog is already visible, this will bring the dialog * to the front. * <p> * If the dialog is modal and is not already visible, this call will * not return until the dialog is hidden by calling <code>hide</code> or * <code>dispose</code>. It is permissible to show modal dialogs from * the event dispatching thread because the toolkit will ensure that * another event pump runs while the one which invoked this method * is blocked. * @see Component#hide * @see Component#isDisplayable * @see Component#validate * @see java.awt.Dialog#isModal */ public void show() { beforeFirstShow = false; if (!isModal()) { conditionalShow(); } else { // Set this variable before calling conditionalShow(). That // way, if the Dialog is hidden right after being shown, we // won't mistakenly block this thread. keepBlocking = true; // LMK app context support // Store the app context on which this dialog is being shown. // Event dispatch thread of this app context will be sleeping until // we wake it by any event from hideAndDisposeHandler(). showAppContext = AppContext.getAppContext(); if (conditionalShow()) { // We have two mechanisms for blocking: 1. If we're on the // EventDispatchThread, start a new event pump. 2. If we're // on any other thread, call wait() on the treelock. // LMK added for proper focus mgmt behavior // keep the KeyEvents from being dispatched // until the focus has been transfered long time = Toolkit.getEventQueue().getMostRecentEventTime(); Component predictedFocusOwner = getMostRecentFocusOwner(); KeyboardFocusManager.getCurrentKeyboardFocusManager(). enqueueKeyEvents(time, predictedFocusOwner); if (Toolkit.getEventQueue().isDispatchThread()) { // LMK added to make sequenced events work properly /* * dispose SequencedEvent we are dispatching on current * AppContext, to prevent us from hang. * * BugId 4531693 (son@sparc.spb.su) */ SequencedEvent currentSequencedEvent = KeyboardFocusManager. getCurrentKeyboardFocusManager().getCurrentSequencedEvent(); if (currentSequencedEvent != null) { System.out.println("disposing sequencedEvent"); currentSequencedEvent.dispose(); } EventDispatchThread dispatchThread = (EventDispatchThread) Thread.currentThread(); dispatchThread.pumpEvents(new Conditional() { public boolean evaluate() { return keepBlocking; } } ); } else { synchronized (getTreeLock()) { while (keepBlocking) { try { getTreeLock().wait(); } catch (InterruptedException e) { break; } } } } // LMK added for proper focus mgmt behavior KeyboardFocusManager.getCurrentKeyboardFocusManager(). dequeueKeyEvents(time, predictedFocusOwner); } } } // LMK app context support final static class WakingRunnable implements Runnable { public void run() { } } private void hideAndDisposeHandler() { if (keepBlocking) { synchronized (getTreeLock()) { keepBlocking = false; // LMK app context support if (showAppContext != null) { // Wake up event dispatch thread on which the dialog was // initially shown SunToolkit.postEvent(showAppContext, new PeerEvent(this, new WakingRunnable(), PeerEvent.PRIORITY_EVENT)); showAppContext = null; } EventQueue.invokeLater(new Runnable() { public void run() {} } ); getTreeLock().notifyAll(); } } } /** * Hides the Dialog and then causes show() to return if it is currently * blocked. */ public void hide() { super.hide(); hideAndDisposeHandler(); } /** * Disposes the Dialog and then causes show() to return if it is currently * blocked. */ public void dispose() { super.dispose(); hideAndDisposeHandler(); } /** * Indicates whether this dialog is resizable by the user. * @return <code>true</code> if the user can resize the dialog; * <code>false</code> otherwise. * @see java.awt.Dialog#setResizable */ public boolean isResizable() { return resizable; } /** * Sets whether this dialog is resizable by the user. * <p> * <em>Note: This operation is subject to * <a href="#restrictions">restriction</a> * in Personal Profile.</em> * * @param resizable <code>true</code> if the user can * resize this dialog; <code>false</code> otherwise. * @see java.awt.Dialog#isResizable */ public synchronized void setResizable(boolean resizable) { this.resizable = resizable; DialogPeer peer = (DialogPeer) this.peer; if (peer != null) { peer.setResizable(resizable); } } /** * Disables or enables decorations for this dialog. * This method can only be called while the dialog is not displayable. * @param undecorated <code>true</code> if no dialog decorations are * to be enabled; * <code>false</code> if dialog decorations are to be enabled. * @throws <code>IllegalComponentStateException</code> if the dialog * is displayable. * @see #isUndecorated * @see Component#isDisplayable * @since 1.4 */ public void setUndecorated(boolean undecorated) { /* Make sure we don't run in the middle of peer creation.*/ synchronized (getTreeLock()) { if (isDisplayable()) { throw new IllegalComponentStateException("The dialog is displayable."); } this.undecorated = undecorated; } } /** * Indicates whether this dialog is undecorated. * By default, all dialogs are initially decorated. * @return <code>true</code> if dialog is undecorated; * <code>false</code> otherwise. * @see java.awt.Dialog#setUndecorated * @since 1.4 */ public boolean isUndecorated() { return undecorated; } /** * Returns the parameter string representing the state of this * dialog window. This string is useful for debugging. * @return the parameter string of this dialog window. */ protected String paramString() { String str = super.paramString() + (modal ? ",modal" : ",modeless"); if (title != null) { str += ",title=" + title; } return str; } private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); setModal(this.modal); } }