1. /*
  2. * @(#)Dialog.java 1.69 01/04/21
  3. *
  4. * Copyright 1995-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt;
  11. import java.awt.peer.DialogPeer;
  12. import java.awt.event.*;
  13. import java.io.ObjectOutputStream;
  14. import java.io.ObjectInputStream;
  15. import java.io.IOException;
  16. import javax.accessibility.*;
  17. /**
  18. * A Dialog is a top-level window with a title and a border
  19. * that is typically used to take some form of input from the user.
  20. *
  21. * The size of the dialog includes any area designated for the
  22. * border. The dimensions of the border area can be obtained
  23. * using the <code>getInsets</code> method, however, since
  24. * these dimensions are platform-dependent, a valid insets
  25. * value cannot be obtained until the dialog is made displayable
  26. * by either calling <code>pack</code> or <code>show</code>.
  27. * Since the border area is included in the overall size of the
  28. * dialog, the border effectively obscures a portion of the dialog,
  29. * constraining the area available for rendering and/or displaying
  30. * subcomponents to the rectangle which has an upper-left corner
  31. * location of <code>(insets.left, insets.top)</code>, and has a size of
  32. * <code>width - (insets.left + insets.right)</code> by
  33. * <code>height - (insets.top + insets.bottom)</code>.
  34. * <p>
  35. * The default layout for a dialog is BorderLayout.
  36. * <p>
  37. * A dialog must have either a frame or another dialog defined as its
  38. * owner when it's constructed. When the owner window of a visible dialog
  39. * is hidden or minimized, the dialog will automatically be hidden
  40. * from the user. When the owner window is subsequently re-opened, then
  41. * the dialog is made visible to the user again.
  42. * <p>
  43. * A dialog can be either modeless (the default) or modal. A modal
  44. * dialog is one which blocks input to all other toplevel windows
  45. * in the app context, except for any windows created with the dialog
  46. * as their owner.
  47. * <p>
  48. * Dialogs are capable of generating the following window events:
  49. * WindowOpened, WindowClosing, WindowClosed, WindowActivated,
  50. * WindowDeactivated.
  51. *
  52. * @see WindowEvent
  53. * @see Window#addWindowListener
  54. *
  55. * @version 1.69, 04/21/01
  56. * @author Sami Shaio
  57. * @author Arthur van Hoff
  58. * @since JDK1.0
  59. */
  60. public class Dialog extends Window {
  61. static {
  62. /* ensure that the necessary native libraries are loaded */
  63. Toolkit.loadLibraries();
  64. initIDs();
  65. }
  66. /**
  67. * A dialog's resizable property. Will be true
  68. * if the Dialog is to be resizable, otherwise
  69. * it will be false.
  70. *
  71. * @serial
  72. * @see setResizable()
  73. */
  74. boolean resizable = true;
  75. /**
  76. * Will be true if the Dialog is modal,
  77. * otherwise the dialog will be modeless.
  78. * A modal Dialog grabs all the input to
  79. * the owner frame from the user.
  80. *
  81. * @serial
  82. * @see isModal()
  83. * @see setModal()
  84. */
  85. boolean modal;
  86. /**
  87. * Specifies the title of the Dialog.
  88. * This field can be null.
  89. *
  90. * @serial
  91. * @see getTitle()
  92. * @see setTitle()
  93. */
  94. String title;
  95. private transient boolean keepBlocking = false;
  96. private static final String base = "dialog";
  97. private static int nameCounter = 0;
  98. /*
  99. * JDK 1.1 serialVersionUID
  100. */
  101. private static final long serialVersionUID = 5920926903803293709L;
  102. /**
  103. * Constructs an initially invisible, non-modal Dialog with
  104. * an empty title and the specified owner frame.
  105. * @param owner the owner of the dialog
  106. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  107. * is <code>null</code>
  108. * @see Component#setSize
  109. * @see Component#setVisible
  110. */
  111. public Dialog(Frame owner) {
  112. this(owner, "", false);
  113. }
  114. /**
  115. * Constructs an initially invisible Dialog with an empty title,
  116. * the specified owner frame and modality.
  117. * @param owner the owner of the dialog
  118. * @param modal if true, dialog blocks input to other app windows when shown
  119. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  120. * is <code>null</code>
  121. */
  122. public Dialog(Frame owner, boolean modal) {
  123. this(owner, "", modal);
  124. }
  125. /**
  126. * Constructs an initially invisible, non-modal Dialog with
  127. * the specified owner frame and title.
  128. * @param owner the owner of the dialog
  129. * @param title the title of the dialog. A <code>null</code> value
  130. * will be accepted without causing a NullPointerException
  131. * to be thrown.
  132. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  133. * is <code>null</code>
  134. * @see Component#setSize
  135. * @see Component#setVisible
  136. */
  137. public Dialog(Frame owner, String title) {
  138. this(owner, title, false);
  139. }
  140. /**
  141. * Constructs an initially invisible Dialog with the
  142. * specified owner frame, title, and modality.
  143. * @param owner the owner of the dialog
  144. * @param title the title of the dialog. A <code>null</code> value
  145. * will be accepted without causing a NullPointerException
  146. * to be thrown.
  147. * @param modal if true, dialog blocks input to other app windows when shown
  148. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  149. * is <code>null</code>
  150. * @see Component#setSize
  151. * @see Component#setVisible
  152. */
  153. public Dialog(Frame owner, String title, boolean modal) {
  154. super(owner);
  155. this.title = title;
  156. this.modal = modal;
  157. }
  158. /**
  159. * Constructs an initially invisible, non-modal Dialog with
  160. * an empty title and the specified owner dialog.
  161. * @param owner the owner of the dialog
  162. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  163. * is <code>null</code>
  164. * @since 1.2
  165. */
  166. public Dialog(Dialog owner) {
  167. this(owner, "", false);
  168. }
  169. /**
  170. * Constructs an initially invisible, non-modal Dialog
  171. * with the specified owner dialog and title.
  172. * @param owner the owner of the dialog
  173. * @param title the title of the dialog. A <code>null</code> value
  174. * will be accepted without causing a NullPointerException
  175. * to be thrown.
  176. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  177. * is <code>null</code>
  178. * @since 1.2
  179. */
  180. public Dialog(Dialog owner, String title) {
  181. this(owner, title, false);
  182. }
  183. /**
  184. * Constructs an initially invisible Dialog with the
  185. * specified owner dialog, title, and modality.
  186. * @param owner the owner of the dialog
  187. * @param title the title of the dialog. A <code>null</code> value
  188. * will be accepted without causing a NullPointerException to
  189. * be thrown.
  190. * @param modal if true, dialog blocks input to other app windows when shown
  191. * @exception java.lang.IllegalArgumentException if <code>owner</code>
  192. * is <code>null</code>
  193. * @since 1.2
  194. */
  195. public Dialog(Dialog owner, String title, boolean modal) {
  196. super(owner);
  197. this.title = title;
  198. this.modal = modal;
  199. }
  200. /**
  201. * Construct a name for this component. Called by getName() when the
  202. * name is null.
  203. */
  204. String constructComponentName() {
  205. synchronized (getClass()) {
  206. return base + nameCounter++;
  207. }
  208. }
  209. /**
  210. * Makes this Dialog displayable by connecting it to
  211. * a native screen resource. Making a dialog displayable will
  212. * cause any of its children to be made displayable.
  213. * This method is called internally by the toolkit and should
  214. * not be called directly by programs.
  215. * @see Component#isDisplayable
  216. * @see #removeNotify
  217. */
  218. public void addNotify() {
  219. synchronized (getTreeLock()) {
  220. if (parent != null && parent.getPeer() == null) {
  221. parent.addNotify();
  222. }
  223. if (peer == null) {
  224. peer = getToolkit().createDialog(this);
  225. }
  226. super.addNotify();
  227. }
  228. }
  229. /**
  230. * Indicates whether the dialog is modal.
  231. * When a modal Dialog is made visible, user input will be
  232. * blocked to the other windows in the app context, except for
  233. * any windows created with this dialog as their owner.
  234. *
  235. * @return <code>true</code> if this dialog window is modal;
  236. * <code>false</code> otherwise.
  237. * @see java.awt.Dialog#setModal
  238. */
  239. public boolean isModal() {
  240. return modal;
  241. }
  242. /**
  243. * Specifies whether this dialog should be modal.
  244. * @see java.awt.Dialog#isModal
  245. * @since JDK1.1
  246. */
  247. public void setModal(boolean b) {
  248. this.modal = b;
  249. }
  250. /**
  251. * Gets the title of the dialog. The title is displayed in the
  252. * dialog's border.
  253. * @return the title of this dialog window. The title may be
  254. * <code>null</code>.
  255. * @see java.awt.Dialog#setTitle
  256. */
  257. public String getTitle() {
  258. return title;
  259. }
  260. /**
  261. * Sets the title of the Dialog.
  262. * @param title the title displayed in the dialog's border
  263. * @see #getTitle
  264. */
  265. public synchronized void setTitle(String title) {
  266. this.title = title;
  267. DialogPeer peer = (DialogPeer)this.peer;
  268. if (peer != null) {
  269. peer.setTitle(title);
  270. }
  271. }
  272. /**
  273. * @return true if we actually showed, false if we just called toFront()
  274. */
  275. private boolean conditionalShow() {
  276. boolean retval;
  277. synchronized (getTreeLock()) {
  278. if (peer == null) {
  279. addNotify();
  280. }
  281. validate();
  282. if (visible) {
  283. toFront();
  284. retval = false;
  285. } else {
  286. visible = retval = true;
  287. peer.show(); // now guaranteed never to block
  288. createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
  289. this, parent,
  290. HierarchyEvent.SHOWING_CHANGED);
  291. }
  292. if (retval && (componentListener != null ||
  293. (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0)) {
  294. ComponentEvent e =
  295. new ComponentEvent(this, ComponentEvent.COMPONENT_SHOWN);
  296. Toolkit.getEventQueue().postEvent(e);
  297. }
  298. }
  299. if (retval && (state & OPENED) == 0) {
  300. postWindowEvent(WindowEvent.WINDOW_OPENED);
  301. state |= OPENED;
  302. }
  303. return retval;
  304. }
  305. /**
  306. * Makes the Dialog visible. If the dialog and/or its owner
  307. * are not yet displayable, both are made displayable. The
  308. * dialog will be validated prior to being made visible.
  309. * If the dialog is already visible, this will bring the dialog
  310. * to the front.
  311. * <p>
  312. * If the dialog is modal and is not already visible, this call will
  313. * not return until the dialog is hidden by calling <code>hide</code> or
  314. * <code>dispose</code>. It is permissible to show modal dialogs from
  315. * the event dispatching thread because the toolkit will ensure that
  316. * another event pump runs while the one which invoked this method
  317. * is blocked.
  318. * @see Component#hide
  319. * @see Component#isDisplayable
  320. * @see Component#validate
  321. * @see java.awt.Dialog#isModal
  322. */
  323. public void show() {
  324. if (!isModal()) {
  325. conditionalShow();
  326. } else {
  327. // Set this variable before calling conditionalShow(). That
  328. // way, if the Dialog is hidden right after being shown, we
  329. // won't mistakenly block this thread.
  330. keepBlocking = true;
  331. if (conditionalShow()) {
  332. // We have two mechanisms for blocking: 1. If we're on the
  333. // EventDispatchThread, start a new event pump. 2. If we're
  334. // on any other thread, call wait() on the treelock.
  335. if (Toolkit.getEventQueue().isDispatchThread()) {
  336. EventDispatchThread dispatchThread =
  337. (EventDispatchThread)Thread.currentThread();
  338. /*
  339. * pump events, filter out input events for
  340. * component not belong to our modal dialog.
  341. *
  342. * we already disabled other components in native code
  343. * but because the event is posted from a different
  344. * thread so it's possible that there are some events
  345. * for other component already posted in the queue
  346. * before we decide do modal show.
  347. */
  348. dispatchThread.pumpEventsForHierarchy(new Conditional() {
  349. public boolean evaluate() {
  350. return keepBlocking && windowClosingException == null;
  351. }
  352. }, this);
  353. } else {
  354. synchronized (getTreeLock()) {
  355. while (keepBlocking && windowClosingException == null) {
  356. try {
  357. getTreeLock().wait();
  358. } catch (InterruptedException e) {
  359. break;
  360. }
  361. }
  362. }
  363. }
  364. if (windowClosingException != null) {
  365. windowClosingException.fillInStackTrace();
  366. throw windowClosingException;
  367. }
  368. }
  369. }
  370. }
  371. void interruptBlocking() {
  372. if (modal) {
  373. disposeImpl();
  374. } else if (windowClosingException != null) {
  375. windowClosingException.fillInStackTrace();
  376. windowClosingException.printStackTrace();
  377. windowClosingException = null;
  378. }
  379. }
  380. private void hideAndDisposeHandler() {
  381. if (keepBlocking) {
  382. synchronized (getTreeLock()) {
  383. keepBlocking = false;
  384. EventQueue.invokeLater(new Runnable(){ public void run() {} });
  385. getTreeLock().notifyAll();
  386. }
  387. }
  388. }
  389. /**
  390. * Hides the Dialog and then causes show() to return if it is currently
  391. * blocked.
  392. */
  393. public void hide() {
  394. super.hide();
  395. hideAndDisposeHandler();
  396. }
  397. /**
  398. * Disposes the Dialog and then causes show() to return if it is currently
  399. * blocked.
  400. */
  401. public void dispose() {
  402. disposeImpl();
  403. }
  404. private void disposeImpl() {
  405. super.dispose();
  406. hideAndDisposeHandler();
  407. }
  408. /**
  409. * Indicates whether this dialog is resizable by the user.
  410. * @return <code>true</code> if the user can resize the dialog;
  411. * <code>false</code> otherwise.
  412. * @see java.awt.Dialog#setResizable
  413. */
  414. public boolean isResizable() {
  415. return resizable;
  416. }
  417. /**
  418. * Sets whether this dialog is resizable by the user.
  419. * @param resizable <code>true</code> if the user can
  420. * resize this dialog; <code>false</code> otherwise.
  421. * @see java.awt.Dialog#isResizable
  422. */
  423. public void setResizable(boolean resizable) {
  424. boolean testvalid = false;
  425. synchronized (this) {
  426. this.resizable = resizable;
  427. DialogPeer peer = (DialogPeer)this.peer;
  428. if (peer != null) {
  429. peer.setResizable(resizable);
  430. testvalid = true;
  431. }
  432. }
  433. // On some platforms, changing the resizable state affects
  434. // the insets of the Dialog. If we could, we'd call invalidate()
  435. // from the peer, but we need to guarantee that we're not holding
  436. // the Dialog lock when we call invalidate().
  437. if (testvalid && valid) {
  438. invalidate();
  439. }
  440. }
  441. /**
  442. * Returns the parameter string representing the state of this
  443. * dialog window. This string is useful for debugging.
  444. * @return the parameter string of this dialog window.
  445. */
  446. protected String paramString() {
  447. String str = super.paramString() + (modal ? ",modal" : ",modeless");
  448. if (title != null) {
  449. str += ",title=" + title;
  450. }
  451. return str;
  452. }
  453. /**
  454. * Initialize JNI field and method IDs
  455. */
  456. private static native void initIDs();
  457. /*
  458. * --- Accessibility Support ---
  459. *
  460. */
  461. /**
  462. * Gets the AccessibleContext associated with this Dialog.
  463. * For dialogs, the AccessibleContext takes the form of an
  464. * AccessibleAWTDialog.
  465. * A new AccessibleAWTDialog instance is created if necessary.
  466. *
  467. * @return an AccessibleAWTDialog that serves as the
  468. * AccessibleContext of this Dialog
  469. */
  470. public AccessibleContext getAccessibleContext() {
  471. if (accessibleContext == null) {
  472. accessibleContext = new AccessibleAWTDialog();
  473. }
  474. return accessibleContext;
  475. }
  476. /**
  477. * This class implements accessibility support for the
  478. * <code>Dialog</code> class. It provides an implementation of the
  479. * Java Accessibility API appropriate to dialog user-interface elements.
  480. */
  481. protected class AccessibleAWTDialog extends AccessibleAWTWindow {
  482. /**
  483. * Get the role of this object.
  484. *
  485. * @return an instance of AccessibleRole describing the role of the
  486. * object
  487. * @see AccessibleRole
  488. */
  489. public AccessibleRole getAccessibleRole() {
  490. return AccessibleRole.DIALOG;
  491. }
  492. /**
  493. * Get the state of this object.
  494. *
  495. * @return an instance of AccessibleStateSet containing the current
  496. * state set of the object
  497. * @see AccessibleState
  498. */
  499. public AccessibleStateSet getAccessibleStateSet() {
  500. AccessibleStateSet states = super.getAccessibleStateSet();
  501. if (getFocusOwner() != null) {
  502. states.add(AccessibleState.ACTIVE);
  503. }
  504. if (isModal()) {
  505. states.add(AccessibleState.MODAL);
  506. }
  507. if (isResizable()) {
  508. states.add(AccessibleState.RESIZABLE);
  509. }
  510. return states;
  511. }
  512. } // inner class AccessibleAWTDialog
  513. }