1 /*
   2  * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 
  28 package javax.swing;
  29 
  30 
  31 
  32 import java.io.*;
  33 import java.awt.BorderLayout;
  34 import java.awt.Frame;
  35 import java.awt.Dialog;
  36 import java.awt.Window;
  37 import java.awt.Component;
  38 import java.awt.Container;
  39 import java.beans.PropertyChangeEvent;
  40 import java.beans.PropertyChangeListener;
  41 import java.awt.event.WindowListener;
  42 import java.awt.event.WindowAdapter;
  43 import java.awt.event.WindowEvent;
  44 
  45 import java.awt.IllegalComponentStateException;
  46 import java.awt.Point;
  47 import java.awt.Rectangle;
  48 import java.text.*;
  49 import java.util.Locale;
  50 import javax.accessibility.*;
  51 import javax.swing.event.*;
  52 import javax.swing.text.*;
  53 
  54 
  55 /** A class to monitor the progress of some operation. If it looks
  56  * like the operation will take a while, a progress dialog will be popped up.
  57  * When the ProgressMonitor is created it is given a numeric range and a
  58  * descriptive string. As the operation progresses, call the setProgress method
  59  * to indicate how far along the [min,max] range the operation is.
  60  * Initially, there is no ProgressDialog. After the first millisToDecideToPopup
  61  * milliseconds (default 500) the progress monitor will predict how long
  62  * the operation will take.  If it is longer than millisToPopup (default 2000,
  63  * 2 seconds) a ProgressDialog will be popped up.
  64  * <p>
  65  * From time to time, when the Dialog box is visible, the progress bar will
  66  * be updated when setProgress is called.  setProgress won't always update
  67  * the progress bar, it will only be done if the amount of progress is
  68  * visibly significant.
  69  *
  70  * <p>
  71  *
  72  * For further documentation and examples see
  73  * <a
  74  href="http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html">How to Monitor Progress</a>,
  75  * a section in <em>The Java Tutorial.</em>
  76  *
  77  * @see ProgressMonitorInputStream
  78  * @author James Gosling
  79  * @author Lynn Monsanto (accessibility)
  80  */
  81 public class ProgressMonitor implements Accessible
  82 {
  83     private ProgressMonitor root;
  84     private JDialog         dialog;
  85     private JOptionPane     pane;
  86     private JProgressBar    myBar;
  87     private JLabel          noteLabel;
  88     private Component       parentComponent;
  89     private String          note;
  90     private Object[]        cancelOption = null;
  91     private Object          message;
  92     private long            T0;
  93     private int             millisToDecideToPopup = 500;
  94     private int             millisToPopup = 2000;
  95     private int             min;
  96     private int             max;
  97 
  98 
  99     /**
 100      * Constructs a graphic object that shows progress, typically by filling
 101      * in a rectangular bar as the process nears completion.
 102      *
 103      * @param parentComponent the parent component for the dialog box
 104      * @param message a descriptive message that will be shown
 105      *        to the user to indicate what operation is being monitored.
 106      *        This does not change as the operation progresses.
 107      *        See the message parameters to methods in
 108      *        {@link JOptionPane#message}
 109      *        for the range of values.
 110      * @param note a short note describing the state of the
 111      *        operation.  As the operation progresses, you can call
 112      *        setNote to change the note displayed.  This is used,
 113      *        for example, in operations that iterate through a
 114      *        list of files to show the name of the file being processes.
 115      *        If note is initially null, there will be no note line
 116      *        in the dialog box and setNote will be ineffective
 117      * @param min the lower bound of the range
 118      * @param max the upper bound of the range
 119      * @see JDialog
 120      * @see JOptionPane
 121      */
 122     public ProgressMonitor(Component parentComponent,
 123                            Object message,
 124                            String note,
 125                            int min,
 126                            int max) {
 127         this(parentComponent, message, note, min, max, null);
 128     }
 129 
 130 
 131     private ProgressMonitor(Component parentComponent,
 132                             Object message,
 133                             String note,
 134                             int min,
 135                             int max,
 136                             ProgressMonitor group) {
 137         this.min = min;
 138         this.max = max;
 139         this.parentComponent = parentComponent;
 140 
 141         cancelOption = new Object[1];
 142         cancelOption[0] = UIManager.getString("OptionPane.cancelButtonText");
 143 
 144         this.message = message;
 145         this.note = note;
 146         if (group != null) {
 147             root = (group.root != null) ? group.root : group;
 148             T0 = root.T0;
 149             dialog = root.dialog;
 150         }
 151         else {
 152             T0 = System.currentTimeMillis();
 153         }
 154     }
 155 
 156 
 157     private class ProgressOptionPane extends JOptionPane
 158     {
 159         ProgressOptionPane(Object messageList) {
 160             super(messageList,
 161                   JOptionPane.INFORMATION_MESSAGE,
 162                   JOptionPane.DEFAULT_OPTION,
 163                   null,
 164                   ProgressMonitor.this.cancelOption,
 165                   null);
 166         }
 167 
 168 
 169         public int getMaxCharactersPerLineCount() {
 170             return 60;
 171         }
 172 
 173 
 174         // Equivalent to JOptionPane.createDialog,
 175         // but create a modeless dialog.
 176         // This is necessary because the Solaris implementation doesn't
 177         // support Dialog.setModal yet.
 178         public JDialog createDialog(Component parentComponent, String title) {
 179             final JDialog dialog;
 180 
 181             Window window = JOptionPane.getWindowForComponent(parentComponent);
 182             if (window instanceof Frame) {
 183                 dialog = new JDialog((Frame)window, title, false);
 184             } else {
 185                 dialog = new JDialog((Dialog)window, title, false);
 186             }
 187             if (window instanceof SwingUtilities.SharedOwnerFrame) {
 188                 WindowListener ownerShutdownListener =
 189                         SwingUtilities.getSharedOwnerFrameShutdownListener();
 190                 dialog.addWindowListener(ownerShutdownListener);
 191             }
 192             Container contentPane = dialog.getContentPane();
 193 
 194             contentPane.setLayout(new BorderLayout());
 195             contentPane.add(this, BorderLayout.CENTER);
 196             dialog.pack();
 197             dialog.setLocationRelativeTo(parentComponent);
 198             dialog.addWindowListener(new WindowAdapter() {
 199                 boolean gotFocus = false;
 200 
 201                 public void windowClosing(WindowEvent we) {
 202                     setValue(cancelOption[0]);
 203                 }
 204 
 205                 public void windowActivated(WindowEvent we) {
 206                     // Once window gets focus, set initial focus
 207                     if (!gotFocus) {
 208                         selectInitialValue();
 209                         gotFocus = true;
 210                     }
 211                 }
 212             });
 213 
 214             addPropertyChangeListener(new PropertyChangeListener() {
 215                 public void propertyChange(PropertyChangeEvent event) {
 216                     if(dialog.isVisible() &&
 217                        event.getSource() == ProgressOptionPane.this &&
 218                        (event.getPropertyName().equals(VALUE_PROPERTY) ||
 219                         event.getPropertyName().equals(INPUT_VALUE_PROPERTY))){
 220                         dialog.setVisible(false);
 221                         dialog.dispose();
 222                     }
 223                 }
 224             });
 225 
 226             return dialog;
 227         }
 228 
 229         /////////////////
 230         // Accessibility support for ProgressOptionPane
 231         ////////////////
 232 
 233         /**
 234          * Gets the AccessibleContext for the ProgressOptionPane
 235          *
 236          * @return the AccessibleContext for the ProgressOptionPane
 237          * @since 1.5
 238          */
 239         public AccessibleContext getAccessibleContext() {
 240             return ProgressMonitor.this.getAccessibleContext();
 241         }
 242 
 243         /*
 244          * Returns the AccessibleJOptionPane
 245          */
 246         private AccessibleContext getAccessibleJOptionPane() {
 247             return super.getAccessibleContext();
 248         }
 249     }
 250 
 251 
 252     /**
 253      * Indicate the progress of the operation being monitored.
 254      * If the specified value is &gt;= the maximum, the progress
 255      * monitor is closed.
 256      * @param nv an int specifying the current value, between the
 257      *        maximum and minimum specified for this component
 258      * @see #setMinimum
 259      * @see #setMaximum
 260      * @see #close
 261      */
 262     public void setProgress(int nv) {
 263         if (nv >= max) {
 264             close();
 265         }
 266         else {
 267             if (myBar != null) {
 268                 myBar.setValue(nv);
 269             }
 270             else {
 271                 long T = System.currentTimeMillis();
 272                 long dT = (int)(T-T0);
 273                 if (dT >= millisToDecideToPopup) {
 274                     int predictedCompletionTime;
 275                     if (nv > min) {
 276                         predictedCompletionTime = (int)(dT *
 277                                                         (max - min) /
 278                                                         (nv - min));
 279                     }
 280                     else {
 281                         predictedCompletionTime = millisToPopup;
 282                     }
 283                     if (predictedCompletionTime >= millisToPopup) {
 284                         myBar = new JProgressBar();
 285                         myBar.setMinimum(min);
 286                         myBar.setMaximum(max);
 287                         myBar.setValue(nv);
 288                         if (note != null) noteLabel = new JLabel(note);
 289                         pane = new ProgressOptionPane(new Object[] {message,
 290                                                                     noteLabel,
 291                                                                     myBar});
 292                         dialog = pane.createDialog(parentComponent,
 293                             UIManager.getString(
 294                                 "ProgressMonitor.progressText"));
 295                         dialog.show();
 296                     }
 297                 }
 298             }
 299         }
 300     }
 301 
 302 
 303     /**
 304      * Indicate that the operation is complete.  This happens automatically
 305      * when the value set by setProgress is &gt;= max, but it may be called
 306      * earlier if the operation ends early.
 307      */
 308     public void close() {
 309         if (dialog != null) {
 310             dialog.setVisible(false);
 311             dialog.dispose();
 312             dialog = null;
 313             pane = null;
 314             myBar = null;
 315         }
 316     }
 317 
 318 
 319     /**
 320      * Returns the minimum value -- the lower end of the progress value.
 321      *
 322      * @return an int representing the minimum value
 323      * @see #setMinimum
 324      */
 325     public int getMinimum() {
 326         return min;
 327     }
 328 
 329 
 330     /**
 331      * Specifies the minimum value.
 332      *
 333      * @param m  an int specifying the minimum value
 334      * @see #getMinimum
 335      */
 336     public void setMinimum(int m) {
 337         if (myBar != null) {
 338             myBar.setMinimum(m);
 339         }
 340         min = m;
 341     }
 342 
 343 
 344     /**
 345      * Returns the maximum value -- the higher end of the progress value.
 346      *
 347      * @return an int representing the maximum value
 348      * @see #setMaximum
 349      */
 350     public int getMaximum() {
 351         return max;
 352     }
 353 
 354 
 355     /**
 356      * Specifies the maximum value.
 357      *
 358      * @param m  an int specifying the maximum value
 359      * @see #getMaximum
 360      */
 361     public void setMaximum(int m) {
 362         if (myBar != null) {
 363             myBar.setMaximum(m);
 364         }
 365         max = m;
 366     }
 367 
 368 
 369     /**
 370      * Returns true if the user hits the Cancel button in the progress dialog.
 371      */
 372     public boolean isCanceled() {
 373         if (pane == null) return false;
 374         Object v = pane.getValue();
 375         return ((v != null) &&
 376                 (cancelOption.length == 1) &&
 377                 (v.equals(cancelOption[0])));
 378     }
 379 
 380 
 381     /**
 382      * Specifies the amount of time to wait before deciding whether or
 383      * not to popup a progress monitor.
 384      *
 385      * @param millisToDecideToPopup  an int specifying the time to wait,
 386      *        in milliseconds
 387      * @see #getMillisToDecideToPopup
 388      */
 389     public void setMillisToDecideToPopup(int millisToDecideToPopup) {
 390         this.millisToDecideToPopup = millisToDecideToPopup;
 391     }
 392 
 393 
 394     /**
 395      * Returns the amount of time this object waits before deciding whether
 396      * or not to popup a progress monitor.
 397      *
 398      * @see #setMillisToDecideToPopup
 399      */
 400     public int getMillisToDecideToPopup() {
 401         return millisToDecideToPopup;
 402     }
 403 
 404 
 405     /**
 406      * Specifies the amount of time it will take for the popup to appear.
 407      * (If the predicted time remaining is less than this time, the popup
 408      * won't be displayed.)
 409      *
 410      * @param millisToPopup  an int specifying the time in milliseconds
 411      * @see #getMillisToPopup
 412      */
 413     public void setMillisToPopup(int millisToPopup) {
 414         this.millisToPopup = millisToPopup;
 415     }
 416 
 417 
 418     /**
 419      * Returns the amount of time it will take for the popup to appear.
 420      *
 421      * @see #setMillisToPopup
 422      */
 423     public int getMillisToPopup() {
 424         return millisToPopup;
 425     }
 426 
 427 
 428     /**
 429      * Specifies the additional note that is displayed along with the
 430      * progress message. Used, for example, to show which file the
 431      * is currently being copied during a multiple-file copy.
 432      *
 433      * @param note  a String specifying the note to display
 434      * @see #getNote
 435      */
 436     public void setNote(String note) {
 437         this.note = note;
 438         if (noteLabel != null) {
 439             noteLabel.setText(note);
 440         }
 441     }
 442 
 443 
 444     /**
 445      * Specifies the additional note that is displayed along with the
 446      * progress message.
 447      *
 448      * @return a String specifying the note to display
 449      * @see #setNote
 450      */
 451     public String getNote() {
 452         return note;
 453     }
 454 
 455     /////////////////
 456     // Accessibility support
 457     ////////////////
 458 
 459     /**
 460      * The <code>AccessibleContext</code> for the <code>ProgressMonitor</code>
 461      * @since 1.5
 462      */
 463     protected AccessibleContext accessibleContext = null;
 464 
 465     private AccessibleContext accessibleJOptionPane = null;
 466 
 467     /**
 468      * Gets the <code>AccessibleContext</code> for the
 469      * <code>ProgressMonitor</code>
 470      *
 471      * @return the <code>AccessibleContext</code> for the
 472      * <code>ProgressMonitor</code>
 473      * @since 1.5
 474      */
 475     public AccessibleContext getAccessibleContext() {
 476         if (accessibleContext == null) {
 477             accessibleContext = new AccessibleProgressMonitor();
 478         }
 479         if (pane != null && accessibleJOptionPane == null) {
 480             // Notify the AccessibleProgressMonitor that the
 481             // ProgressOptionPane was created. It is necessary
 482             // to poll for ProgressOptionPane creation because
 483             // the ProgressMonitor does not have a Component
 484             // to add a listener to until the ProgressOptionPane
 485             // is created.
 486             if (accessibleContext instanceof AccessibleProgressMonitor) {
 487                 ((AccessibleProgressMonitor)accessibleContext).optionPaneCreated();
 488             }
 489         }
 490         return accessibleContext;
 491     }
 492 
 493     /**
 494      * <code>AccessibleProgressMonitor</code> implements accessibility
 495      * support for the <code>ProgressMonitor</code> class.
 496      * @since 1.5
 497      */
 498     protected class AccessibleProgressMonitor extends AccessibleContext
 499         implements AccessibleText, ChangeListener, PropertyChangeListener {
 500 
 501         /*
 502          * The accessibility hierarchy for ProgressMonitor is a flattened
 503          * version of the ProgressOptionPane component hierarchy.
 504          *
 505          * The ProgressOptionPane component hierarchy is:
 506          *   JDialog
 507          *     ProgressOptionPane
 508          *       JPanel
 509          *         JPanel
 510          *           JLabel
 511          *           JLabel
 512          *           JProgressBar
 513          *
 514          * The AccessibleProgessMonitor accessibility hierarchy is:
 515          *   AccessibleJDialog
 516          *     AccessibleProgressMonitor
 517          *       AccessibleJLabel
 518          *       AccessibleJLabel
 519          *       AccessibleJProgressBar
 520          *
 521          * The abstraction presented to assitive technologies by
 522          * the AccessibleProgressMonitor is that a dialog contains a
 523          * progress monitor with three children: a message, a note
 524          * label and a progress bar.
 525          */
 526 
 527         private Object oldModelValue;
 528 
 529         /**
 530          * AccessibleProgressMonitor constructor
 531          */
 532         protected AccessibleProgressMonitor() {
 533         }
 534 
 535         /*
 536          * Initializes the AccessibleContext now that the ProgressOptionPane
 537          * has been created. Because the ProgressMonitor is not a Component
 538          * implementing the Accessible interface, an AccessibleContext
 539          * must be synthesized from the ProgressOptionPane and its children.
 540          *
 541          * For other AWT and Swing classes, the inner class that implements
 542          * accessibility for the class extends the inner class that implements
 543          * implements accessibility for the super class. AccessibleProgressMonitor
 544          * cannot extend AccessibleJOptionPane and must therefore delegate calls
 545          * to the AccessibleJOptionPane.
 546          */
 547         private void optionPaneCreated() {
 548             accessibleJOptionPane =
 549                 ((ProgressOptionPane)pane).getAccessibleJOptionPane();
 550 
 551             // add a listener for progress bar ChangeEvents
 552             if (myBar != null) {
 553                 myBar.addChangeListener(this);
 554             }
 555 
 556             // add a listener for note label PropertyChangeEvents
 557             if (noteLabel != null) {
 558                 noteLabel.addPropertyChangeListener(this);
 559             }
 560         }
 561 
 562         /**
 563          * Invoked when the target of the listener has changed its state.
 564          *
 565          * @param e  a <code>ChangeEvent</code> object. Must not be null.
 566          * @throws NullPointerException if the parameter is null.
 567          */
 568         public void stateChanged(ChangeEvent e) {
 569             if (e == null) {
 570                 return;
 571             }
 572             if (myBar != null) {
 573                 // the progress bar value changed
 574                 Object newModelValue = myBar.getValue();
 575                 firePropertyChange(ACCESSIBLE_VALUE_PROPERTY,
 576                                    oldModelValue,
 577                                    newModelValue);
 578                 oldModelValue = newModelValue;
 579             }
 580         }
 581 
 582         /**
 583          * This method gets called when a bound property is changed.
 584          *
 585          * @param e A <code>PropertyChangeEvent</code> object describing
 586          * the event source and the property that has changed. Must not be null.
 587          * @throws NullPointerException if the parameter is null.
 588          */
 589         public void propertyChange(PropertyChangeEvent e) {
 590             if (e.getSource() == noteLabel && e.getPropertyName() == "text") {
 591                 // the note label text changed
 592                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, 0);
 593             }
 594         }
 595 
 596         /* ===== Begin AccessileContext ===== */
 597 
 598         /**
 599          * Gets the accessibleName property of this object.  The accessibleName
 600          * property of an object is a localized String that designates the purpose
 601          * of the object.  For example, the accessibleName property of a label
 602          * or button might be the text of the label or button itself.  In the
 603          * case of an object that doesn't display its name, the accessibleName
 604          * should still be set.  For example, in the case of a text field used
 605          * to enter the name of a city, the accessibleName for the en_US locale
 606          * could be 'city.'
 607          *
 608          * @return the localized name of the object; null if this
 609          * object does not have a name
 610          *
 611          * @see #setAccessibleName
 612          */
 613         public String getAccessibleName() {
 614             if (accessibleName != null) { // defined in AccessibleContext
 615                 return accessibleName;
 616             } else if (accessibleJOptionPane != null) {
 617                 // delegate to the AccessibleJOptionPane
 618                 return accessibleJOptionPane.getAccessibleName();
 619             }
 620             return null;
 621         }
 622 
 623         /**
 624          * Gets the accessibleDescription property of this object.  The
 625          * accessibleDescription property of this object is a short localized
 626          * phrase describing the purpose of the object.  For example, in the
 627          * case of a 'Cancel' button, the accessibleDescription could be
 628          * 'Ignore changes and close dialog box.'
 629          *
 630          * @return the localized description of the object; null if
 631          * this object does not have a description
 632          *
 633          * @see #setAccessibleDescription
 634          */
 635         public String getAccessibleDescription() {
 636             if (accessibleDescription != null) { // defined in AccessibleContext
 637                 return accessibleDescription;
 638             } else if (accessibleJOptionPane != null) {
 639                 // delegate to the AccessibleJOptionPane
 640                 return accessibleJOptionPane.getAccessibleDescription();
 641             }
 642             return null;
 643         }
 644 
 645         /**
 646          * Gets the role of this object.  The role of the object is the generic
 647          * purpose or use of the class of this object.  For example, the role
 648          * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in
 649          * AccessibleRole are provided so component developers can pick from
 650          * a set of predefined roles.  This enables assistive technologies to
 651          * provide a consistent interface to various tweaked subclasses of
 652          * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
 653          * that act like a push button) as well as distinguish between sublasses
 654          * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
 655          * and AccessibleRole.RADIO_BUTTON for radio buttons).
 656          * <p>Note that the AccessibleRole class is also extensible, so
 657          * custom component developers can define their own AccessibleRole's
 658          * if the set of predefined roles is inadequate.
 659          *
 660          * @return an instance of AccessibleRole describing the role of the object
 661          * @see AccessibleRole
 662          */
 663         public AccessibleRole getAccessibleRole() {
 664             return AccessibleRole.PROGRESS_MONITOR;
 665         }
 666 
 667         /**
 668          * Gets the state set of this object.  The AccessibleStateSet of an object
 669          * is composed of a set of unique AccessibleStates.  A change in the
 670          * AccessibleStateSet of an object will cause a PropertyChangeEvent to
 671          * be fired for the ACCESSIBLE_STATE_PROPERTY property.
 672          *
 673          * @return an instance of AccessibleStateSet containing the
 674          * current state set of the object
 675          * @see AccessibleStateSet
 676          * @see AccessibleState
 677          * @see #addPropertyChangeListener
 678          */
 679         public AccessibleStateSet getAccessibleStateSet() {
 680             if (accessibleJOptionPane != null) {
 681                 // delegate to the AccessibleJOptionPane
 682                 return accessibleJOptionPane.getAccessibleStateSet();
 683             }
 684             return null;
 685         }
 686 
 687         /**
 688          * Gets the Accessible parent of this object.
 689          *
 690          * @return the Accessible parent of this object; null if this
 691          * object does not have an Accessible parent
 692          */
 693         public Accessible getAccessibleParent() {
 694             return dialog;
 695         }
 696 
 697         /*
 698          * Returns the parent AccessibleContext
 699          */
 700         private AccessibleContext getParentAccessibleContext() {
 701             if (dialog != null) {
 702                 return dialog.getAccessibleContext();
 703             }
 704             return null;
 705         }
 706 
 707         /**
 708          * Gets the 0-based index of this object in its accessible parent.
 709          *
 710          * @return the 0-based index of this object in its parent; -1 if this
 711          * object does not have an accessible parent.
 712          *
 713          * @see #getAccessibleParent
 714          * @see #getAccessibleChildrenCount
 715          * @see #getAccessibleChild
 716          */
 717         public int getAccessibleIndexInParent() {
 718             if (accessibleJOptionPane != null) {
 719                 // delegate to the AccessibleJOptionPane
 720                 return accessibleJOptionPane.getAccessibleIndexInParent();
 721             }
 722             return -1;
 723         }
 724 
 725         /**
 726          * Returns the number of accessible children of the object.
 727          *
 728          * @return the number of accessible children of the object.
 729          */
 730         public int getAccessibleChildrenCount() {
 731             // return the number of children in the JPanel containing
 732             // the message, note label and progress bar
 733             AccessibleContext ac = getPanelAccessibleContext();
 734             if (ac != null) {
 735                 return ac.getAccessibleChildrenCount();
 736             }
 737             return 0;
 738         }
 739 
 740         /**
 741          * Returns the specified Accessible child of the object.  The Accessible
 742          * children of an Accessible object are zero-based, so the first child
 743          * of an Accessible child is at index 0, the second child is at index 1,
 744          * and so on.
 745          *
 746          * @param i zero-based index of child
 747          * @return the Accessible child of the object
 748          * @see #getAccessibleChildrenCount
 749          */
 750         public Accessible getAccessibleChild(int i) {
 751             // return a child in the JPanel containing the message, note label
 752             // and progress bar
 753             AccessibleContext ac = getPanelAccessibleContext();
 754             if (ac != null) {
 755                 return ac.getAccessibleChild(i);
 756             }
 757             return null;
 758         }
 759 
 760         /*
 761          * Returns the AccessibleContext for the JPanel containing the
 762          * message, note label and progress bar
 763          */
 764         private AccessibleContext getPanelAccessibleContext() {
 765             if (myBar != null) {
 766                 Component c = myBar.getParent();
 767                 if (c instanceof Accessible) {
 768                     return c.getAccessibleContext();
 769                 }
 770             }
 771             return null;
 772         }
 773 
 774         /**
 775          * Gets the locale of the component. If the component does not have a
 776          * locale, then the locale of its parent is returned.
 777          *
 778          * @return this component's locale.  If this component does not have
 779          * a locale, the locale of its parent is returned.
 780          *
 781          * @exception IllegalComponentStateException
 782          * If the Component does not have its own locale and has not yet been
 783          * added to a containment hierarchy such that the locale can be
 784          * determined from the containing parent.
 785          */
 786         public Locale getLocale() throws IllegalComponentStateException {
 787             if (accessibleJOptionPane != null) {
 788                 // delegate to the AccessibleJOptionPane
 789                 return accessibleJOptionPane.getLocale();
 790             }
 791             return null;
 792         }
 793 
 794         /* ===== end AccessibleContext ===== */
 795 
 796         /**
 797          * Gets the AccessibleComponent associated with this object that has a
 798          * graphical representation.
 799          *
 800          * @return AccessibleComponent if supported by object; else return null
 801          * @see AccessibleComponent
 802          */
 803         public AccessibleComponent getAccessibleComponent() {
 804             if (accessibleJOptionPane != null) {
 805                 // delegate to the AccessibleJOptionPane
 806                 return accessibleJOptionPane.getAccessibleComponent();
 807             }
 808             return null;
 809         }
 810 
 811         /**
 812          * Gets the AccessibleValue associated with this object that supports a
 813          * Numerical value.
 814          *
 815          * @return AccessibleValue if supported by object; else return null
 816          * @see AccessibleValue
 817          */
 818         public AccessibleValue getAccessibleValue() {
 819             if (myBar != null) {
 820                 // delegate to the AccessibleJProgressBar
 821                 return myBar.getAccessibleContext().getAccessibleValue();
 822             }
 823             return null;
 824         }
 825 
 826         /**
 827          * Gets the AccessibleText associated with this object presenting
 828          * text on the display.
 829          *
 830          * @return AccessibleText if supported by object; else return null
 831          * @see AccessibleText
 832          */
 833         public AccessibleText getAccessibleText() {
 834             if (getNoteLabelAccessibleText() != null) {
 835                 return this;
 836             }
 837             return null;
 838         }
 839 
 840         /*
 841          * Returns the note label AccessibleText
 842          */
 843         private AccessibleText getNoteLabelAccessibleText() {
 844             if (noteLabel != null) {
 845                 // AccessibleJLabel implements AccessibleText if the
 846                 // JLabel contains HTML text
 847                 return noteLabel.getAccessibleContext().getAccessibleText();
 848             }
 849             return null;
 850         }
 851 
 852         /* ===== Begin AccessibleText impl ===== */
 853 
 854         /**
 855          * Given a point in local coordinates, return the zero-based index
 856          * of the character under that Point.  If the point is invalid,
 857          * this method returns -1.
 858          *
 859          * @param p the Point in local coordinates
 860          * @return the zero-based index of the character under Point p; if
 861          * Point is invalid return -1.
 862          */
 863         public int getIndexAtPoint(Point p) {
 864             AccessibleText at = getNoteLabelAccessibleText();
 865             if (at != null && sameWindowAncestor(pane, noteLabel)) {
 866                 // convert point from the option pane bounds
 867                 // to the note label bounds.
 868                 Point noteLabelPoint = SwingUtilities.convertPoint(pane,
 869                                                                    p,
 870                                                                    noteLabel);
 871                 if (noteLabelPoint != null) {
 872                     return at.getIndexAtPoint(noteLabelPoint);
 873                 }
 874             }
 875             return -1;
 876         }
 877 
 878         /**
 879          * Determines the bounding box of the character at the given
 880          * index into the string.  The bounds are returned in local
 881          * coordinates.  If the index is invalid an empty rectangle is returned.
 882          *
 883          * @param i the index into the String
 884          * @return the screen coordinates of the character's bounding box,
 885          * if index is invalid return an empty rectangle.
 886          */
 887         public Rectangle getCharacterBounds(int i) {
 888             AccessibleText at = getNoteLabelAccessibleText();
 889             if (at != null && sameWindowAncestor(pane, noteLabel)) {
 890                 // return rectangle in the option pane bounds
 891                 Rectangle noteLabelRect = at.getCharacterBounds(i);
 892                 if (noteLabelRect != null) {
 893                     return SwingUtilities.convertRectangle(noteLabel,
 894                                                            noteLabelRect,
 895                                                            pane);
 896                 }
 897             }
 898             return null;
 899         }
 900 
 901         /*
 902          * Returns whether source and destination components have the
 903          * same window ancestor
 904          */
 905         private boolean sameWindowAncestor(Component src, Component dest) {
 906             if (src == null || dest == null) {
 907                 return false;
 908             }
 909             return SwingUtilities.getWindowAncestor(src) ==
 910                 SwingUtilities.getWindowAncestor(dest);
 911         }
 912 
 913         /**
 914          * Returns the number of characters (valid indicies)
 915          *
 916          * @return the number of characters
 917          */
 918         public int getCharCount() {
 919             AccessibleText at = getNoteLabelAccessibleText();
 920             if (at != null) {   // JLabel contains HTML text
 921                 return at.getCharCount();
 922             }
 923             return -1;
 924         }
 925 
 926         /**
 927          * Returns the zero-based offset of the caret.
 928          *
 929          * Note: That to the right of the caret will have the same index
 930          * value as the offset (the caret is between two characters).
 931          * @return the zero-based offset of the caret.
 932          */
 933         public int getCaretPosition() {
 934             AccessibleText at = getNoteLabelAccessibleText();
 935             if (at != null) {   // JLabel contains HTML text
 936                 return at.getCaretPosition();
 937             }
 938             return -1;
 939         }
 940 
 941         /**
 942          * Returns the String at a given index.
 943          *
 944          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
 945          * @param index an index within the text
 946          * @return the letter, word, or sentence
 947          */
 948         public String getAtIndex(int part, int index) {
 949             AccessibleText at = getNoteLabelAccessibleText();
 950             if (at != null) {   // JLabel contains HTML text
 951                 return at.getAtIndex(part, index);
 952             }
 953             return null;
 954         }
 955 
 956         /**
 957          * Returns the String after a given index.
 958          *
 959          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
 960          * @param index an index within the text
 961          * @return the letter, word, or sentence
 962          */
 963         public String getAfterIndex(int part, int index) {
 964             AccessibleText at = getNoteLabelAccessibleText();
 965             if (at != null) {   // JLabel contains HTML text
 966                 return at.getAfterIndex(part, index);
 967             }
 968             return null;
 969         }
 970 
 971         /**
 972          * Returns the String before a given index.
 973          *
 974          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
 975          * @param index an index within the text
 976          * @return the letter, word, or sentence
 977          */
 978         public String getBeforeIndex(int part, int index) {
 979             AccessibleText at = getNoteLabelAccessibleText();
 980             if (at != null) {   // JLabel contains HTML text
 981                 return at.getBeforeIndex(part, index);
 982             }
 983             return null;
 984         }
 985 
 986         /**
 987          * Returns the AttributeSet for a given character at a given index
 988          *
 989          * @param i the zero-based index into the text
 990          * @return the AttributeSet of the character
 991          */
 992         public AttributeSet getCharacterAttribute(int i) {
 993             AccessibleText at = getNoteLabelAccessibleText();
 994             if (at != null) {   // JLabel contains HTML text
 995                 return at.getCharacterAttribute(i);
 996             }
 997             return null;
 998         }
 999 
1000         /**
1001          * Returns the start offset within the selected text.
1002          * If there is no selection, but there is
1003          * a caret, the start and end offsets will be the same.
1004          *
1005          * @return the index into the text of the start of the selection
1006          */
1007         public int getSelectionStart() {
1008             AccessibleText at = getNoteLabelAccessibleText();
1009             if (at != null) {   // JLabel contains HTML text
1010                 return at.getSelectionStart();
1011             }
1012             return -1;
1013         }
1014 
1015         /**
1016          * Returns the end offset within the selected text.
1017          * If there is no selection, but there is
1018          * a caret, the start and end offsets will be the same.
1019          *
1020          * @return the index into teh text of the end of the selection
1021          */
1022         public int getSelectionEnd() {
1023             AccessibleText at = getNoteLabelAccessibleText();
1024             if (at != null) {   // JLabel contains HTML text
1025                 return at.getSelectionEnd();
1026             }
1027             return -1;
1028         }
1029 
1030         /**
1031          * Returns the portion of the text that is selected.
1032          *
1033          * @return the String portion of the text that is selected
1034          */
1035         public String getSelectedText() {
1036             AccessibleText at = getNoteLabelAccessibleText();
1037             if (at != null) {   // JLabel contains HTML text
1038                 return at.getSelectedText();
1039             }
1040             return null;
1041         }
1042         /* ===== End AccessibleText impl ===== */
1043     }
1044     // inner class AccessibleProgressMonitor
1045 
1046 }