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