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