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