001/* A top-level dialog window containing an arbitrary component. 002 003 Copyright (c) 1998-2016 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027 028// In addition portions of the WindowClosingAdapter are derived from 029// https://docs.oracle.com/javase/tutorial/uiswing/examples/components/DialogDemoProject/src/components/CustomDialog.java 030// which has the following license: 031 032/* 033 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. 034 * 035 * Redistribution and use in source and binary forms, with or without 036 * modification, are permitted provided that the following conditions 037 * are met: 038 * 039 * - Redistributions of source code must retain the above copyright 040 * notice, this list of conditions and the following disclaimer. 041 * 042 * - Redistributions in binary form must reproduce the above copyright 043 * notice, this list of conditions and the following disclaimer in the 044 * documentation and/or other materials provided with the distribution. 045 * 046 * - Neither the name of Oracle or the names of its 047 * contributors may be used to endorse or promote products derived 048 * from this software without specific prior written permission. 049 * 050 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 051 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 052 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 053 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 054 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 055 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 056 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 057 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 058 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 059 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 060 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 061 */ 062 063package ptolemy.gui; 064 065import java.awt.Component; 066import java.awt.Dimension; 067import java.awt.Font; 068import java.awt.Frame; 069import java.awt.Toolkit; 070import java.awt.event.WindowAdapter; 071import java.awt.event.WindowEvent; 072import java.beans.PropertyChangeEvent; 073import java.beans.PropertyChangeListener; 074 075import javax.swing.Box; 076import javax.swing.BoxLayout; 077import javax.swing.JDialog; 078import javax.swing.JOptionPane; 079import javax.swing.JPanel; 080import javax.swing.JTextArea; 081 082/////////////////////////////////////////////////////////////////// 083//// ComponentDialog 084 085/** 086 087 This class is a modal dialog box that contains an arbitrary component. 088 It can be used, for example, to put an instance of Query in a 089 top-level dialog box. The general way to use this class is to create 090 the component that you wish to have contained in the dialog. 091 Then pass that component to the constructor of this class. The dialog 092 is modal, so the statement that creates the dialog will not return 093 until the user dismisses the dialog. The method buttonPressed() 094 can then be called to find out whether the user clicked the OK button 095 or the Cancel button (or any other button specified in the constructor). 096 Then you can access the component to determine what values were set 097 by the user. 098 <p> 099 If the component that is added implements the CloseListener interface, 100 then that component is notified when this dialog closes. 101 102 @see CloseListener 103 @author Edward A. Lee 104 @version $Id$ 105 @since Ptolemy II 0.4 106 @Pt.ProposedRating Yellow (eal) 107 @Pt.AcceptedRating Yellow (janneck) 108 */ 109@SuppressWarnings("serial") 110public class ComponentDialog extends JDialog { 111 /** Construct a dialog with the specified owner, title, and component. 112 * An "OK" and a "Cancel" button are added to the dialog. 113 * The dialog is placed relative to the owner. 114 * @param owner The object that, per the user, appears to be 115 * generating the dialog. 116 * @param title The title of the dialog. 117 * @param component The component to insert in the dialog. 118 */ 119 public ComponentDialog(Frame owner, String title, Component component) { 120 this(owner, title, component, null, null); 121 } 122 123 /** Construct a dialog with the specified owner, title, component, 124 * and buttons. The first button is the "default" in that 125 * it is the one activated by "Enter" or "Return" keys. 126 * If the last argument is null, then an "OK" 127 * and a "Cancel" button will be created. 128 * The dialog is placed relative to the owner. 129 * @param owner The object that, per the user, appears to be 130 * generating the dialog. 131 * @param title The title of the dialog. 132 * @param component The component to insert in the dialog. 133 * @param buttons An array of labels for buttons at the bottom 134 * of the dialog. 135 */ 136 public ComponentDialog(Frame owner, String title, Component component, 137 String[] buttons) { 138 this(owner, title, component, buttons, null); 139 } 140 141 /** Construct a dialog with the specified owner, title, component, 142 * buttons, and message. The message is placed above the component. 143 * The first button is the "default" in that 144 * it is the one activated by "Enter" or "Return" keys. 145 * If the <i>buttons</i> argument is null, then an "OK" 146 * and a "Cancel" button will be created. 147 * The dialog is placed relative to the owner. 148 * @param owner The object that, per the user, appears to be 149 * generating the dialog. 150 * @param title The title of the dialog. 151 * @param component The component to insert in the dialog. 152 * @param buttons An array of labels for buttons at the bottom 153 * of the dialog. 154 * @param message A message to place above the component, or null 155 * if no message is needed. 156 */ 157 public ComponentDialog(Frame owner, String title, Component component, 158 String[] buttons, String message) { 159 this(owner, title, component, buttons, message, false); 160 } 161 162 /** Construct a dialog with the specified owner, title, component, 163 * buttons, and message. The message is placed above the component. 164 * The first button is the "default" in that 165 * it is the one activated by "Enter" or "Return" keys. 166 * If the <i>buttons</i> argument is null, then an "OK" 167 * and a "Cancel" button will be created. 168 * The dialog is placed relative to the owner. 169 * @param owner The object that, per the user, appears to be 170 * generating the dialog. 171 * @param title The title of the dialog. 172 * @param component The component to insert in the dialog. 173 * @param buttons An array of labels for buttons at the bottom 174 * of the dialog. 175 * @param message A message to place above the component, or null 176 * if no message is needed. 177 * @param resizable True to allow the dialog to be resized. 178 */ 179 public ComponentDialog(Frame owner, String title, Component component, 180 String[] buttons, String message, boolean resizable) { 181 super(owner, title, true); 182 183 // Create a panel that contains the optional message 184 // and the specified component, separated by a spacing rigid area. 185 JPanel panel = new JPanel(); 186 187 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 188 189 if (message != null) { 190 _messageArea = new JTextArea(message); 191 _messageArea.setFont(new Font("SansSerif", Font.PLAIN, 12)); 192 _messageArea.setEditable(false); 193 _messageArea.setLineWrap(true); 194 _messageArea.setWrapStyleWord(true); 195 _messageArea.setBackground(getContentPane().getBackground()); 196 197 // Left Justify. 198 _messageArea.setAlignmentX(0.0f); 199 panel.add(_messageArea); 200 panel.add(Box.createRigidArea(new Dimension(0, 10))); 201 } 202 203 panel.add(component); 204 contents = component; 205 206 if (buttons != null) { 207 _buttons = buttons; 208 } else { 209 _buttons = _defaultButtons; 210 } 211 212 _optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, 213 JOptionPane.YES_NO_OPTION, null, _buttons, _buttons[0]); 214 215 // The following code is based on Sun's CustomDialog example... 216 _propChangeListener = new PropChangeListener(); 217 _optionPane.addPropertyChangeListener(_propChangeListener); 218 219 getContentPane().add(_optionPane); 220 pack(); 221 setResizable(true); 222 223 if (owner != null) { 224 setLocationRelativeTo(owner); 225 } else { 226 // Center on screen. According to the Java docs, 227 // passing null to setLocationRelationTo() _may_ result 228 // in centering on the screen, but it is not required to. 229 Toolkit tk = Toolkit.getDefaultToolkit(); 230 setLocation((tk.getScreenSize().width - getSize().width) / 2, 231 (tk.getScreenSize().height - getSize().height) / 2); 232 } 233 234 // NOTE: Java's AWT may yield random results if we do the following. 235 // And anyway, it doesn't work. Components still don't 236 // have their ComponentListener methods called to indicate 237 // that they have become invisible. 238 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 239 240 // Catch closing events so that components are notified if 241 // the window manager is used to close the window. 242 _windowClosingAdapter = new WindowClosingAdapter(); 243 addWindowListener(_windowClosingAdapter); 244 245 // Make the window visible. 246 setVisible(true); 247 } 248 249 /////////////////////////////////////////////////////////////////// 250 //// public methods //// 251 252 /** Return the label of the button that triggered closing the 253 * dialog, or an empty string if none. 254 * @return The label of the button pressed. 255 */ 256 public String buttonPressed() { 257 return _buttonPressed; 258 } 259 260 @Override 261 public void dispose() { 262 _optionPane.removePropertyChangeListener(_propChangeListener); 263 _propChangeListener = null; 264 this.removeWindowListener(_windowClosingAdapter); 265 _windowClosingAdapter = null; 266 getContentPane().removeAll(); 267 super.dispose(); 268 } 269 270 /////////////////////////////////////////////////////////////////// 271 //// protected methods //// 272 273 /** Change the message that was specified in the constructor to 274 * read as specified. If no message was specified in the constructor, 275 * then do nothing. 276 * @param message The new message. 277 */ 278 public void setMessage(String message) { 279 if (_messageArea != null) { 280 _messageArea.setText(message); 281 } 282 } 283 284 /////////////////////////////////////////////////////////////////// 285 //// public variables //// 286 287 /** The component contained by this dialog. 288 */ 289 public Component contents; 290 291 /////////////////////////////////////////////////////////////////// 292 //// protected methods //// 293 294 /** If the contents of this dialog implements the CloseListener 295 * interface, then notify it that the window has closed, unless 296 * notification has already been done (it is guaranteed to be done 297 * only once). 298 */ 299 protected void _handleClosing() { 300 // Close the window. 301 setVisible(false); 302 dispose(); 303 304 if (contents instanceof CloseListener && !_doneHandleClosing) { 305 _doneHandleClosing = true; 306 ((CloseListener) contents).windowClosed(this, _buttonPressed); 307 } 308 } 309 310 /////////////////////////////////////////////////////////////////// 311 //// protected variables //// 312 313 /** The label of the button pushed to dismiss the dialog. */ 314 protected String _buttonPressed = ""; 315 316 /** A reference to the WindowClosingAdapter.*/ 317 protected WindowClosingAdapter _windowClosingAdapter; 318 319 /** A reference to the PropertyChangeListener.*/ 320 protected PropChangeListener _propChangeListener; 321 322 /////////////////////////////////////////////////////////////////// 323 //// private variables //// 324 325 /** Button labels. */ 326 private static String[] _buttons; 327 328 /** Default button labels. */ 329 private static String[] _defaultButtons = { "OK", "Cancel" }; 330 331 /** Indicator that we have notified of window closing. */ 332 private boolean _doneHandleClosing = false; 333 334 /** The pane with the buttons. */ 335 private JOptionPane _optionPane; 336 337 /** The container for messages. */ 338 private JTextArea _messageArea; 339 340 /////////////////////////////////////////////////////////////////// 341 //// inner classes //// 342 343 /** Listener for windowClosing action. */ 344 class WindowClosingAdapter extends WindowAdapter { 345 @Override 346 public void windowClosing(WindowEvent e) { 347 _handleClosing(); 348 } 349 } 350 351 /** Listen for property changes. 352 * This inner class is derived from 353 * https://docs.oracle.com/javase/tutorial/uiswing/examples/components/DialogDemoProject/src/components/CustomDialog.java 354 * See the top of this file for the CustomDialog.java license. 355 */ 356 class PropChangeListener implements PropertyChangeListener { 357 @Override 358 public void propertyChange(PropertyChangeEvent e) { 359 String prop = e.getPropertyName(); 360 361 // PropertyChange is an extremely non-selective listener, 362 // so we have to filter... 363 if (isVisible() && e.getSource() == _optionPane 364 && (prop.equals(JOptionPane.VALUE_PROPERTY) 365 || prop.equals(JOptionPane.INPUT_VALUE_PROPERTY))) { 366 Object value = _optionPane.getValue(); 367 368 // Reset should be ignored. 369 if (value == JOptionPane.UNINITIALIZED_VALUE) { 370 return; 371 } 372 373 // The value of JOptionPane is reset. 374 // Resetting is required, otherwise when 375 // the button is pressed again, a property 376 // change event will not be fired. 377 // Note that this seems to trigger the listener 378 // again, so the previous line is essential. 379 _optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE); 380 381 if (value instanceof String) { 382 // A button was pressed... 383 _buttonPressed = (String) value; 384 } else { 385 _buttonPressed = ""; 386 } 387 388 // Take any action that might be associated with 389 // window closing. 390 _handleClosing(); 391 } 392 } 393 } 394}