Print this page
rev 5696 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o packaging changes. by Naoto Sato and Masayoshi Okutsu)
Split |
Close |
Expand all |
Collapse all |
--- old/src/share/classes/javax/swing/JSpinner.java
+++ new/src/share/classes/javax/swing/JSpinner.java
1 1 /*
2 - * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
2 + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation. Oracle designates this
8 8 * particular file as subject to the "Classpath" exception as provided
9 9 * by Oracle in the LICENSE file that accompanied this code.
10 10 *
11 11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 14 * version 2 for more details (a copy is included in the LICENSE file that
15 15 * accompanied this code).
16 16 *
17 17 * You should have received a copy of the GNU General Public License version
18 18 * 2 along with this work; if not, write to the Free Software Foundation,
19 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 20 *
21 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 22 * or visit www.oracle.com if you need additional information or have any
23 23 * questions.
24 24 */
25 25
26 26 package javax.swing;
27 27
28 28 import java.awt.*;
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
29 29 import java.awt.event.*;
30 30
31 31 import javax.swing.event.*;
32 32 import javax.swing.text.*;
33 33 import javax.swing.plaf.SpinnerUI;
34 34
35 35 import java.util.*;
36 36 import java.beans.*;
37 37 import java.text.*;
38 38 import java.io.*;
39 -import java.util.HashMap;
40 -import sun.util.resources.LocaleData;
39 +import java.text.spi.DateFormatProvider;
40 +import java.text.spi.NumberFormatProvider;
41 41
42 42 import javax.accessibility.*;
43 +import sun.util.locale.provider.LocaleProviderAdapter;
44 +import sun.util.locale.provider.LocaleResources;
45 +import sun.util.locale.provider.LocaleServiceProviderPool;
43 46
44 47
45 48 /**
46 49 * A single line input field that lets the user select a
47 50 * number or an object value from an ordered sequence. Spinners typically
48 51 * provide a pair of tiny arrow buttons for stepping through the elements
49 52 * of the sequence. The keyboard up/down arrow keys also cycle through the
50 53 * elements. The user may also be allowed to type a (legal) value directly
51 54 * into the spinner. Although combo boxes provide similar functionality,
52 55 * spinners are sometimes preferred because they don't require a drop down list
53 56 * that can obscure important data.
54 57 * <p>
55 58 * A <code>JSpinner</code>'s sequence value is defined by its
56 59 * <code>SpinnerModel</code>.
57 60 * The <code>model</code> can be specified as a constructor argument and
58 61 * changed with the <code>model</code> property. <code>SpinnerModel</code>
59 62 * classes for some common types are provided: <code>SpinnerListModel</code>,
60 63 * <code>SpinnerNumberModel</code>, and <code>SpinnerDateModel</code>.
61 64 * <p>
62 65 * A <code>JSpinner</code> has a single child component that's
63 66 * responsible for displaying
64 67 * and potentially changing the current element or <i>value</i> of
65 68 * the model, which is called the <code>editor</code>. The editor is created
66 69 * by the <code>JSpinner</code>'s constructor and can be changed with the
67 70 * <code>editor</code> property. The <code>JSpinner</code>'s editor stays
68 71 * in sync with the model by listening for <code>ChangeEvent</code>s. If the
69 72 * user has changed the value displayed by the <code>editor</code> it is
70 73 * possible for the <code>model</code>'s value to differ from that of
71 74 * the <code>editor</code>. To make sure the <code>model</code> has the same
72 75 * value as the editor use the <code>commitEdit</code> method, eg:
73 76 * <pre>
74 77 * try {
75 78 * spinner.commitEdit();
76 79 * }
77 80 * catch (ParseException pe) {{
78 81 * // Edited value is invalid, spinner.getValue() will return
79 82 * // the last valid value, you could revert the spinner to show that:
80 83 * JComponent editor = spinner.getEditor()
81 84 * if (editor instanceof DefaultEditor) {
82 85 * ((DefaultEditor)editor).getTextField().setValue(spinner.getValue();
83 86 * }
84 87 * // reset the value to some known value:
85 88 * spinner.setValue(fallbackValue);
86 89 * // or treat the last valid value as the current, in which
87 90 * // case you don't need to do anything.
88 91 * }
89 92 * return spinner.getValue();
90 93 * </pre>
91 94 * <p>
92 95 * For information and examples of using spinner see
93 96 * <a href="http://java.sun.com/doc/books/tutorial/uiswing/components/spinner.html">How to Use Spinners</a>,
94 97 * a section in <em>The Java Tutorial.</em>
95 98 * <p>
96 99 * <strong>Warning:</strong> Swing is not thread safe. For more
97 100 * information see <a
98 101 * href="package-summary.html#threading">Swing's Threading
99 102 * Policy</a>.
100 103 * <p>
101 104 * <strong>Warning:</strong>
102 105 * Serialized objects of this class will not be compatible with
103 106 * future Swing releases. The current serialization support is
104 107 * appropriate for short term storage or RMI between applications running
105 108 * the same version of Swing. As of 1.4, support for long term storage
106 109 * of all JavaBeans<sup><font size="-2">TM</font></sup>
107 110 * has been added to the <code>java.beans</code> package.
108 111 * Please see {@link java.beans.XMLEncoder}.
109 112 *
110 113 * @beaninfo
111 114 * attribute: isContainer false
112 115 * description: A single line input field that lets the user select a
113 116 * number or an object value from an ordered set.
114 117 *
115 118 * @see SpinnerModel
116 119 * @see AbstractSpinnerModel
117 120 * @see SpinnerListModel
118 121 * @see SpinnerNumberModel
119 122 * @see SpinnerDateModel
120 123 * @see JFormattedTextField
121 124 *
122 125 * @author Hans Muller
123 126 * @author Lynn Monsanto (accessibility)
124 127 * @since 1.4
125 128 */
126 129 public class JSpinner extends JComponent implements Accessible
127 130 {
128 131 /**
129 132 * @see #getUIClassID
130 133 * @see #readObject
131 134 */
132 135 private static final String uiClassID = "SpinnerUI";
133 136
134 137 private static final Action DISABLED_ACTION = new DisabledAction();
135 138
136 139 private SpinnerModel model;
137 140 private JComponent editor;
138 141 private ChangeListener modelListener;
139 142 private transient ChangeEvent changeEvent;
140 143 private boolean editorExplicitlySet = false;
141 144
142 145
143 146 /**
144 147 * Constructs a spinner for the given model. The spinner has
145 148 * a set of previous/next buttons, and an editor appropriate
146 149 * for the model.
147 150 *
148 151 * @throws NullPointerException if the model is {@code null}
149 152 */
150 153 public JSpinner(SpinnerModel model) {
151 154 if (model == null) {
152 155 throw new NullPointerException("model cannot be null");
153 156 }
154 157 this.model = model;
155 158 this.editor = createEditor(model);
156 159 setUIProperty("opaque",true);
157 160 updateUI();
158 161 }
159 162
160 163
161 164 /**
162 165 * Constructs a spinner with an <code>Integer SpinnerNumberModel</code>
163 166 * with initial value 0 and no minimum or maximum limits.
164 167 */
165 168 public JSpinner() {
166 169 this(new SpinnerNumberModel());
167 170 }
168 171
169 172
170 173 /**
171 174 * Returns the look and feel (L&F) object that renders this component.
172 175 *
173 176 * @return the <code>SpinnerUI</code> object that renders this component
174 177 */
175 178 public SpinnerUI getUI() {
176 179 return (SpinnerUI)ui;
177 180 }
178 181
179 182
180 183 /**
181 184 * Sets the look and feel (L&F) object that renders this component.
182 185 *
183 186 * @param ui the <code>SpinnerUI</code> L&F object
184 187 * @see UIDefaults#getUI
185 188 */
186 189 public void setUI(SpinnerUI ui) {
187 190 super.setUI(ui);
188 191 }
189 192
190 193
191 194 /**
192 195 * Returns the suffix used to construct the name of the look and feel
193 196 * (L&F) class used to render this component.
194 197 *
195 198 * @return the string "SpinnerUI"
196 199 * @see JComponent#getUIClassID
197 200 * @see UIDefaults#getUI
198 201 */
199 202 public String getUIClassID() {
200 203 return uiClassID;
201 204 }
202 205
203 206
204 207
205 208 /**
206 209 * Resets the UI property with the value from the current look and feel.
207 210 *
208 211 * @see UIManager#getUI
209 212 */
210 213 public void updateUI() {
211 214 setUI((SpinnerUI)UIManager.getUI(this));
212 215 invalidate();
213 216 }
214 217
215 218
216 219 /**
217 220 * This method is called by the constructors to create the
218 221 * <code>JComponent</code>
219 222 * that displays the current value of the sequence. The editor may
220 223 * also allow the user to enter an element of the sequence directly.
221 224 * An editor must listen for <code>ChangeEvents</code> on the
222 225 * <code>model</code> and keep the value it displays
223 226 * in sync with the value of the model.
224 227 * <p>
225 228 * Subclasses may override this method to add support for new
226 229 * <code>SpinnerModel</code> classes. Alternatively one can just
227 230 * replace the editor created here with the <code>setEditor</code>
228 231 * method. The default mapping from model type to editor is:
229 232 * <ul>
230 233 * <li> <code>SpinnerNumberModel => JSpinner.NumberEditor</code>
231 234 * <li> <code>SpinnerDateModel => JSpinner.DateEditor</code>
232 235 * <li> <code>SpinnerListModel => JSpinner.ListEditor</code>
233 236 * <li> <i>all others</i> => <code>JSpinner.DefaultEditor</code>
234 237 * </ul>
235 238 *
236 239 * @return a component that displays the current value of the sequence
237 240 * @param model the value of getModel
238 241 * @see #getModel
239 242 * @see #setEditor
240 243 */
241 244 protected JComponent createEditor(SpinnerModel model) {
242 245 if (model instanceof SpinnerDateModel) {
243 246 return new DateEditor(this);
244 247 }
245 248 else if (model instanceof SpinnerListModel) {
246 249 return new ListEditor(this);
247 250 }
248 251 else if (model instanceof SpinnerNumberModel) {
249 252 return new NumberEditor(this);
250 253 }
251 254 else {
252 255 return new DefaultEditor(this);
253 256 }
254 257 }
255 258
256 259
257 260 /**
258 261 * Changes the model that represents the value of this spinner.
259 262 * If the editor property has not been explicitly set,
260 263 * the editor property is (implicitly) set after the <code>"model"</code>
261 264 * <code>PropertyChangeEvent</code> has been fired. The editor
262 265 * property is set to the value returned by <code>createEditor</code>,
263 266 * as in:
264 267 * <pre>
265 268 * setEditor(createEditor(model));
266 269 * </pre>
267 270 *
268 271 * @param model the new <code>SpinnerModel</code>
269 272 * @see #getModel
270 273 * @see #getEditor
271 274 * @see #setEditor
272 275 * @throws IllegalArgumentException if model is <code>null</code>
273 276 *
274 277 * @beaninfo
275 278 * bound: true
276 279 * attribute: visualUpdate true
277 280 * description: Model that represents the value of this spinner.
278 281 */
279 282 public void setModel(SpinnerModel model) {
280 283 if (model == null) {
281 284 throw new IllegalArgumentException("null model");
282 285 }
283 286 if (!model.equals(this.model)) {
284 287 SpinnerModel oldModel = this.model;
285 288 this.model = model;
286 289 if (modelListener != null) {
287 290 oldModel.removeChangeListener(modelListener);
288 291 this.model.addChangeListener(modelListener);
289 292 }
290 293 firePropertyChange("model", oldModel, model);
291 294 if (!editorExplicitlySet) {
292 295 setEditor(createEditor(model)); // sets editorExplicitlySet true
293 296 editorExplicitlySet = false;
294 297 }
295 298 repaint();
296 299 revalidate();
297 300 }
298 301 }
299 302
300 303
301 304 /**
302 305 * Returns the <code>SpinnerModel</code> that defines
303 306 * this spinners sequence of values.
304 307 *
305 308 * @return the value of the model property
306 309 * @see #setModel
307 310 */
308 311 public SpinnerModel getModel() {
309 312 return model;
310 313 }
311 314
312 315
313 316 /**
314 317 * Returns the current value of the model, typically
315 318 * this value is displayed by the <code>editor</code>. If the
316 319 * user has changed the value displayed by the <code>editor</code> it is
317 320 * possible for the <code>model</code>'s value to differ from that of
318 321 * the <code>editor</code>, refer to the class level javadoc for examples
319 322 * of how to deal with this.
320 323 * <p>
321 324 * This method simply delegates to the <code>model</code>.
322 325 * It is equivalent to:
323 326 * <pre>
324 327 * getModel().getValue()
325 328 * </pre>
326 329 *
327 330 * @see #setValue
328 331 * @see SpinnerModel#getValue
329 332 */
330 333 public Object getValue() {
331 334 return getModel().getValue();
332 335 }
333 336
334 337
335 338 /**
336 339 * Changes current value of the model, typically
337 340 * this value is displayed by the <code>editor</code>.
338 341 * If the <code>SpinnerModel</code> implementation
339 342 * doesn't support the specified value then an
340 343 * <code>IllegalArgumentException</code> is thrown.
341 344 * <p>
342 345 * This method simply delegates to the <code>model</code>.
343 346 * It is equivalent to:
344 347 * <pre>
345 348 * getModel().setValue(value)
346 349 * </pre>
347 350 *
348 351 * @throws IllegalArgumentException if <code>value</code> isn't allowed
349 352 * @see #getValue
350 353 * @see SpinnerModel#setValue
351 354 */
352 355 public void setValue(Object value) {
353 356 getModel().setValue(value);
354 357 }
355 358
356 359
357 360 /**
358 361 * Returns the object in the sequence that comes after the object returned
359 362 * by <code>getValue()</code>. If the end of the sequence has been reached
360 363 * then return <code>null</code>.
361 364 * Calling this method does not effect <code>value</code>.
362 365 * <p>
363 366 * This method simply delegates to the <code>model</code>.
364 367 * It is equivalent to:
365 368 * <pre>
366 369 * getModel().getNextValue()
367 370 * </pre>
368 371 *
369 372 * @return the next legal value or <code>null</code> if one doesn't exist
370 373 * @see #getValue
371 374 * @see #getPreviousValue
372 375 * @see SpinnerModel#getNextValue
373 376 */
374 377 public Object getNextValue() {
375 378 return getModel().getNextValue();
376 379 }
377 380
378 381
379 382 /**
380 383 * We pass <code>Change</code> events along to the listeners with the
381 384 * the slider (instead of the model itself) as the event source.
382 385 */
383 386 private class ModelListener implements ChangeListener, Serializable {
384 387 public void stateChanged(ChangeEvent e) {
385 388 fireStateChanged();
386 389 }
387 390 }
388 391
389 392
390 393 /**
391 394 * Adds a listener to the list that is notified each time a change
392 395 * to the model occurs. The source of <code>ChangeEvents</code>
393 396 * delivered to <code>ChangeListeners</code> will be this
394 397 * <code>JSpinner</code>. Note also that replacing the model
395 398 * will not affect listeners added directly to JSpinner.
396 399 * Applications can add listeners to the model directly. In that
397 400 * case is that the source of the event would be the
398 401 * <code>SpinnerModel</code>.
399 402 *
400 403 * @param listener the <code>ChangeListener</code> to add
401 404 * @see #removeChangeListener
402 405 * @see #getModel
403 406 */
404 407 public void addChangeListener(ChangeListener listener) {
405 408 if (modelListener == null) {
406 409 modelListener = new ModelListener();
407 410 getModel().addChangeListener(modelListener);
408 411 }
409 412 listenerList.add(ChangeListener.class, listener);
410 413 }
411 414
412 415
413 416
414 417 /**
415 418 * Removes a <code>ChangeListener</code> from this spinner.
416 419 *
417 420 * @param listener the <code>ChangeListener</code> to remove
418 421 * @see #fireStateChanged
419 422 * @see #addChangeListener
420 423 */
421 424 public void removeChangeListener(ChangeListener listener) {
422 425 listenerList.remove(ChangeListener.class, listener);
423 426 }
424 427
425 428
426 429 /**
427 430 * Returns an array of all the <code>ChangeListener</code>s added
428 431 * to this JSpinner with addChangeListener().
429 432 *
430 433 * @return all of the <code>ChangeListener</code>s added or an empty
431 434 * array if no listeners have been added
432 435 * @since 1.4
433 436 */
434 437 public ChangeListener[] getChangeListeners() {
435 438 return listenerList.getListeners(ChangeListener.class);
436 439 }
437 440
438 441
439 442 /**
440 443 * Sends a <code>ChangeEvent</code>, whose source is this
441 444 * <code>JSpinner</code>, to each <code>ChangeListener</code>.
442 445 * When a <code>ChangeListener</code> has been added
443 446 * to the spinner, this method method is called each time
444 447 * a <code>ChangeEvent</code> is received from the model.
445 448 *
446 449 * @see #addChangeListener
447 450 * @see #removeChangeListener
448 451 * @see EventListenerList
449 452 */
450 453 protected void fireStateChanged() {
451 454 Object[] listeners = listenerList.getListenerList();
452 455 for (int i = listeners.length - 2; i >= 0; i -= 2) {
453 456 if (listeners[i] == ChangeListener.class) {
454 457 if (changeEvent == null) {
455 458 changeEvent = new ChangeEvent(this);
456 459 }
457 460 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
458 461 }
459 462 }
460 463 }
461 464
462 465
463 466 /**
464 467 * Returns the object in the sequence that comes
465 468 * before the object returned by <code>getValue()</code>.
466 469 * If the end of the sequence has been reached then
467 470 * return <code>null</code>. Calling this method does
468 471 * not effect <code>value</code>.
469 472 * <p>
470 473 * This method simply delegates to the <code>model</code>.
471 474 * It is equivalent to:
472 475 * <pre>
473 476 * getModel().getPreviousValue()
474 477 * </pre>
475 478 *
476 479 * @return the previous legal value or <code>null</code>
477 480 * if one doesn't exist
478 481 * @see #getValue
479 482 * @see #getNextValue
480 483 * @see SpinnerModel#getPreviousValue
481 484 */
482 485 public Object getPreviousValue() {
483 486 return getModel().getPreviousValue();
484 487 }
485 488
486 489
487 490 /**
488 491 * Changes the <code>JComponent</code> that displays the current value
489 492 * of the <code>SpinnerModel</code>. It is the responsibility of this
490 493 * method to <i>disconnect</i> the old editor from the model and to
491 494 * connect the new editor. This may mean removing the
492 495 * old editors <code>ChangeListener</code> from the model or the
493 496 * spinner itself and adding one for the new editor.
494 497 *
495 498 * @param editor the new editor
496 499 * @see #getEditor
497 500 * @see #createEditor
498 501 * @see #getModel
499 502 * @throws IllegalArgumentException if editor is <code>null</code>
500 503 *
501 504 * @beaninfo
502 505 * bound: true
503 506 * attribute: visualUpdate true
504 507 * description: JComponent that displays the current value of the model
505 508 */
506 509 public void setEditor(JComponent editor) {
507 510 if (editor == null) {
508 511 throw new IllegalArgumentException("null editor");
509 512 }
510 513 if (!editor.equals(this.editor)) {
511 514 JComponent oldEditor = this.editor;
512 515 this.editor = editor;
513 516 if (oldEditor instanceof DefaultEditor) {
514 517 ((DefaultEditor)oldEditor).dismiss(this);
515 518 }
516 519 editorExplicitlySet = true;
517 520 firePropertyChange("editor", oldEditor, editor);
518 521 revalidate();
519 522 repaint();
520 523 }
521 524 }
522 525
523 526
524 527 /**
525 528 * Returns the component that displays and potentially
526 529 * changes the model's value.
527 530 *
528 531 * @return the component that displays and potentially
529 532 * changes the model's value
530 533 * @see #setEditor
531 534 * @see #createEditor
532 535 */
533 536 public JComponent getEditor() {
534 537 return editor;
535 538 }
536 539
537 540
538 541 /**
539 542 * Commits the currently edited value to the <code>SpinnerModel</code>.
540 543 * <p>
541 544 * If the editor is an instance of <code>DefaultEditor</code>, the
542 545 * call if forwarded to the editor, otherwise this does nothing.
543 546 *
544 547 * @throws ParseException if the currently edited value couldn't
545 548 * be commited.
546 549 */
547 550 public void commitEdit() throws ParseException {
548 551 JComponent editor = getEditor();
549 552 if (editor instanceof DefaultEditor) {
550 553 ((DefaultEditor)editor).commitEdit();
551 554 }
552 555 }
553 556
554 557
555 558 /*
556 559 * See readObject and writeObject in JComponent for more
557 560 * information about serialization in Swing.
558 561 *
559 562 * @param s Stream to write to
560 563 */
561 564 private void writeObject(ObjectOutputStream s) throws IOException {
562 565 s.defaultWriteObject();
563 566 if (getUIClassID().equals(uiClassID)) {
564 567 byte count = JComponent.getWriteObjCounter(this);
565 568 JComponent.setWriteObjCounter(this, --count);
566 569 if (count == 0 && ui != null) {
567 570 ui.installUI(this);
568 571 }
569 572 }
570 573 }
571 574
572 575
573 576 /**
574 577 * A simple base class for more specialized editors
575 578 * that displays a read-only view of the model's current
576 579 * value with a <code>JFormattedTextField</code>. Subclasses
577 580 * can configure the <code>JFormattedTextField</code> to create
578 581 * an editor that's appropriate for the type of model they
579 582 * support and they may want to override
580 583 * the <code>stateChanged</code> and <code>propertyChanged</code>
581 584 * methods, which keep the model and the text field in sync.
582 585 * <p>
583 586 * This class defines a <code>dismiss</code> method that removes the
584 587 * editors <code>ChangeListener</code> from the <code>JSpinner</code>
585 588 * that it's part of. The <code>setEditor</code> method knows about
586 589 * <code>DefaultEditor.dismiss</code>, so if the developer
587 590 * replaces an editor that's derived from <code>JSpinner.DefaultEditor</code>
588 591 * its <code>ChangeListener</code> connection back to the
589 592 * <code>JSpinner</code> will be removed. However after that,
590 593 * it's up to the developer to manage their editor listeners.
591 594 * Similarly, if a subclass overrides <code>createEditor</code>,
592 595 * it's up to the subclasser to deal with their editor
593 596 * subsequently being replaced (with <code>setEditor</code>).
594 597 * We expect that in most cases, and in editor installed
595 598 * with <code>setEditor</code> or created by a <code>createEditor</code>
596 599 * override, will not be replaced anyway.
597 600 * <p>
598 601 * This class is the <code>LayoutManager</code> for it's single
599 602 * <code>JFormattedTextField</code> child. By default the
600 603 * child is just centered with the parents insets.
601 604 * @since 1.4
602 605 */
603 606 public static class DefaultEditor extends JPanel
604 607 implements ChangeListener, PropertyChangeListener, LayoutManager
605 608 {
606 609 /**
607 610 * Constructs an editor component for the specified <code>JSpinner</code>.
608 611 * This <code>DefaultEditor</code> is it's own layout manager and
609 612 * it is added to the spinner's <code>ChangeListener</code> list.
610 613 * The constructor creates a single <code>JFormattedTextField</code> child,
611 614 * initializes it's value to be the spinner model's current value
612 615 * and adds it to <code>this</code> <code>DefaultEditor</code>.
613 616 *
614 617 * @param spinner the spinner whose model <code>this</code> editor will monitor
615 618 * @see #getTextField
616 619 * @see JSpinner#addChangeListener
617 620 */
618 621 public DefaultEditor(JSpinner spinner) {
619 622 super(null);
620 623
621 624 JFormattedTextField ftf = new JFormattedTextField();
622 625 ftf.setName("Spinner.formattedTextField");
623 626 ftf.setValue(spinner.getValue());
624 627 ftf.addPropertyChangeListener(this);
625 628 ftf.setEditable(false);
626 629 ftf.setInheritsPopupMenu(true);
627 630
628 631 String toolTipText = spinner.getToolTipText();
629 632 if (toolTipText != null) {
630 633 ftf.setToolTipText(toolTipText);
631 634 }
632 635
633 636 add(ftf);
634 637
635 638 setLayout(this);
636 639 spinner.addChangeListener(this);
637 640
638 641 // We want the spinner's increment/decrement actions to be
639 642 // active vs those of the JFormattedTextField. As such we
640 643 // put disabled actions in the JFormattedTextField's actionmap.
641 644 // A binding to a disabled action is treated as a nonexistant
642 645 // binding.
643 646 ActionMap ftfMap = ftf.getActionMap();
644 647
645 648 if (ftfMap != null) {
646 649 ftfMap.put("increment", DISABLED_ACTION);
647 650 ftfMap.put("decrement", DISABLED_ACTION);
648 651 }
649 652 }
650 653
651 654
652 655 /**
653 656 * Disconnect <code>this</code> editor from the specified
654 657 * <code>JSpinner</code>. By default, this method removes
655 658 * itself from the spinners <code>ChangeListener</code> list.
656 659 *
657 660 * @param spinner the <code>JSpinner</code> to disconnect this
658 661 * editor from; the same spinner as was passed to the constructor.
659 662 */
660 663 public void dismiss(JSpinner spinner) {
661 664 spinner.removeChangeListener(this);
662 665 }
663 666
664 667
665 668 /**
666 669 * Returns the <code>JSpinner</code> ancestor of this editor or
667 670 * <code>null</code> if none of the ancestors are a
668 671 * <code>JSpinner</code>.
669 672 * Typically the editor's parent is a <code>JSpinner</code> however
670 673 * subclasses of <code>JSpinner</code> may override the
671 674 * the <code>createEditor</code> method and insert one or more containers
672 675 * between the <code>JSpinner</code> and it's editor.
673 676 *
674 677 * @return <code>JSpinner</code> ancestor; <code>null</code>
675 678 * if none of the ancestors are a <code>JSpinner</code>
676 679 *
677 680 * @see JSpinner#createEditor
678 681 */
679 682 public JSpinner getSpinner() {
680 683 for (Component c = this; c != null; c = c.getParent()) {
681 684 if (c instanceof JSpinner) {
682 685 return (JSpinner)c;
683 686 }
684 687 }
685 688 return null;
686 689 }
687 690
688 691
689 692 /**
690 693 * Returns the <code>JFormattedTextField</code> child of this
691 694 * editor. By default the text field is the first and only
692 695 * child of editor.
693 696 *
694 697 * @return the <code>JFormattedTextField</code> that gives the user
695 698 * access to the <code>SpinnerDateModel's</code> value.
696 699 * @see #getSpinner
697 700 * @see #getModel
698 701 */
699 702 public JFormattedTextField getTextField() {
700 703 return (JFormattedTextField)getComponent(0);
701 704 }
702 705
703 706
704 707 /**
705 708 * This method is called when the spinner's model's state changes.
706 709 * It sets the <code>value</code> of the text field to the current
707 710 * value of the spinners model.
708 711 *
709 712 * @param e the <code>ChangeEvent</code> whose source is the
710 713 * <code>JSpinner</code> whose model has changed.
711 714 * @see #getTextField
712 715 * @see JSpinner#getValue
713 716 */
714 717 public void stateChanged(ChangeEvent e) {
715 718 JSpinner spinner = (JSpinner)(e.getSource());
716 719 getTextField().setValue(spinner.getValue());
717 720 }
718 721
719 722
720 723 /**
721 724 * Called by the <code>JFormattedTextField</code>
722 725 * <code>PropertyChangeListener</code>. When the <code>"value"</code>
723 726 * property changes, which implies that the user has typed a new
724 727 * number, we set the value of the spinners model.
725 728 * <p>
726 729 * This class ignores <code>PropertyChangeEvents</code> whose
727 730 * source is not the <code>JFormattedTextField</code>, so subclasses
728 731 * may safely make <code>this</code> <code>DefaultEditor</code> a
729 732 * <code>PropertyChangeListener</code> on other objects.
730 733 *
731 734 * @param e the <code>PropertyChangeEvent</code> whose source is
732 735 * the <code>JFormattedTextField</code> created by this class.
733 736 * @see #getTextField
734 737 */
735 738 public void propertyChange(PropertyChangeEvent e)
736 739 {
737 740 JSpinner spinner = getSpinner();
738 741
739 742 if (spinner == null) {
740 743 // Indicates we aren't installed anywhere.
741 744 return;
742 745 }
743 746
744 747 Object source = e.getSource();
745 748 String name = e.getPropertyName();
746 749 if ((source instanceof JFormattedTextField) && "value".equals(name)) {
747 750 Object lastValue = spinner.getValue();
748 751
749 752 // Try to set the new value
750 753 try {
751 754 spinner.setValue(getTextField().getValue());
752 755 } catch (IllegalArgumentException iae) {
753 756 // SpinnerModel didn't like new value, reset
754 757 try {
755 758 ((JFormattedTextField)source).setValue(lastValue);
756 759 } catch (IllegalArgumentException iae2) {
757 760 // Still bogus, nothing else we can do, the
758 761 // SpinnerModel and JFormattedTextField are now out
759 762 // of sync.
760 763 }
761 764 }
762 765 }
763 766 }
764 767
765 768
766 769 /**
767 770 * This <code>LayoutManager</code> method does nothing. We're
768 771 * only managing a single child and there's no support
769 772 * for layout constraints.
770 773 *
771 774 * @param name ignored
772 775 * @param child ignored
773 776 */
774 777 public void addLayoutComponent(String name, Component child) {
775 778 }
776 779
777 780
778 781 /**
779 782 * This <code>LayoutManager</code> method does nothing. There
780 783 * isn't any per-child state.
781 784 *
782 785 * @param child ignored
783 786 */
784 787 public void removeLayoutComponent(Component child) {
785 788 }
786 789
787 790
788 791 /**
789 792 * Returns the size of the parents insets.
790 793 */
791 794 private Dimension insetSize(Container parent) {
792 795 Insets insets = parent.getInsets();
793 796 int w = insets.left + insets.right;
794 797 int h = insets.top + insets.bottom;
795 798 return new Dimension(w, h);
796 799 }
797 800
798 801
799 802 /**
800 803 * Returns the preferred size of first (and only) child plus the
801 804 * size of the parents insets.
802 805 *
803 806 * @param parent the Container that's managing the layout
804 807 * @return the preferred dimensions to lay out the subcomponents
805 808 * of the specified container.
806 809 */
807 810 public Dimension preferredLayoutSize(Container parent) {
808 811 Dimension preferredSize = insetSize(parent);
809 812 if (parent.getComponentCount() > 0) {
810 813 Dimension childSize = getComponent(0).getPreferredSize();
811 814 preferredSize.width += childSize.width;
812 815 preferredSize.height += childSize.height;
813 816 }
814 817 return preferredSize;
815 818 }
816 819
817 820
818 821 /**
819 822 * Returns the minimum size of first (and only) child plus the
820 823 * size of the parents insets.
821 824 *
822 825 * @param parent the Container that's managing the layout
823 826 * @return the minimum dimensions needed to lay out the subcomponents
824 827 * of the specified container.
825 828 */
826 829 public Dimension minimumLayoutSize(Container parent) {
827 830 Dimension minimumSize = insetSize(parent);
828 831 if (parent.getComponentCount() > 0) {
829 832 Dimension childSize = getComponent(0).getMinimumSize();
830 833 minimumSize.width += childSize.width;
831 834 minimumSize.height += childSize.height;
832 835 }
833 836 return minimumSize;
834 837 }
835 838
836 839
837 840 /**
838 841 * Resize the one (and only) child to completely fill the area
839 842 * within the parents insets.
840 843 */
841 844 public void layoutContainer(Container parent) {
842 845 if (parent.getComponentCount() > 0) {
843 846 Insets insets = parent.getInsets();
844 847 int w = parent.getWidth() - (insets.left + insets.right);
845 848 int h = parent.getHeight() - (insets.top + insets.bottom);
846 849 getComponent(0).setBounds(insets.left, insets.top, w, h);
847 850 }
848 851 }
849 852
850 853 /**
851 854 * Pushes the currently edited value to the <code>SpinnerModel</code>.
852 855 * <p>
853 856 * The default implementation invokes <code>commitEdit</code> on the
854 857 * <code>JFormattedTextField</code>.
855 858 *
856 859 * @throws ParseException if the edited value is not legal
857 860 */
858 861 public void commitEdit() throws ParseException {
859 862 // If the value in the JFormattedTextField is legal, this will have
860 863 // the result of pushing the value to the SpinnerModel
861 864 // by way of the <code>propertyChange</code> method.
862 865 JFormattedTextField ftf = getTextField();
863 866
864 867 ftf.commitEdit();
865 868 }
866 869
867 870 /**
868 871 * Returns the baseline.
869 872 *
870 873 * @throws IllegalArgumentException {@inheritDoc}
871 874 * @see javax.swing.JComponent#getBaseline(int,int)
872 875 * @see javax.swing.JComponent#getBaselineResizeBehavior()
873 876 * @since 1.6
874 877 */
875 878 public int getBaseline(int width, int height) {
876 879 // check size.
877 880 super.getBaseline(width, height);
878 881 Insets insets = getInsets();
879 882 width = width - insets.left - insets.right;
880 883 height = height - insets.top - insets.bottom;
881 884 int baseline = getComponent(0).getBaseline(width, height);
882 885 if (baseline >= 0) {
883 886 return baseline + insets.top;
884 887 }
885 888 return -1;
886 889 }
887 890
888 891 /**
889 892 * Returns an enum indicating how the baseline of the component
890 893 * changes as the size changes.
891 894 *
892 895 * @throws NullPointerException {@inheritDoc}
893 896 * @see javax.swing.JComponent#getBaseline(int, int)
894 897 * @since 1.6
895 898 */
896 899 public BaselineResizeBehavior getBaselineResizeBehavior() {
897 900 return getComponent(0).getBaselineResizeBehavior();
898 901 }
899 902 }
900 903
901 904
902 905
903 906
904 907 /**
905 908 * This subclass of javax.swing.DateFormatter maps the minimum/maximum
906 909 * properties to te start/end properties of a SpinnerDateModel.
907 910 */
908 911 private static class DateEditorFormatter extends DateFormatter {
909 912 private final SpinnerDateModel model;
910 913
911 914 DateEditorFormatter(SpinnerDateModel model, DateFormat format) {
912 915 super(format);
913 916 this.model = model;
914 917 }
915 918
916 919 public void setMinimum(Comparable min) {
917 920 model.setStart(min);
918 921 }
919 922
920 923 public Comparable getMinimum() {
921 924 return model.getStart();
922 925 }
923 926
924 927 public void setMaximum(Comparable max) {
925 928 model.setEnd(max);
926 929 }
927 930
928 931 public Comparable getMaximum() {
929 932 return model.getEnd();
930 933 }
931 934 }
932 935
933 936
934 937 /**
935 938 * An editor for a <code>JSpinner</code> whose model is a
936 939 * <code>SpinnerDateModel</code>. The value of the editor is
937 940 * displayed with a <code>JFormattedTextField</code> whose format
938 941 * is defined by a <code>DateFormatter</code> instance whose
↓ open down ↓ |
886 lines elided |
↑ open up ↑ |
939 942 * <code>minimum</code> and <code>maximum</code> properties
940 943 * are mapped to the <code>SpinnerDateModel</code>.
941 944 * @since 1.4
942 945 */
943 946 // PENDING(hmuller): more example javadoc
944 947 public static class DateEditor extends DefaultEditor
945 948 {
946 949 // This is here until SimpleDateFormat gets a constructor that
947 950 // takes a Locale: 4923525
948 951 private static String getDefaultPattern(Locale loc) {
949 - ResourceBundle r = LocaleData.getDateFormatData(loc);
950 - String[] dateTimePatterns = r.getStringArray("DateTimePatterns");
951 - Object[] dateTimeArgs = {dateTimePatterns[DateFormat.SHORT],
952 - dateTimePatterns[DateFormat.SHORT + 4]};
953 - return MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
952 + LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DateFormatProvider.class, loc);
953 + LocaleResources lr = adapter.getLocaleResources(loc);
954 + if (lr == null) {
955 + lr = LocaleProviderAdapter.forJRE().getLocaleResources(loc);
956 + }
957 + return lr.getDateTimePattern(DateFormat.SHORT, DateFormat.SHORT, null);
954 958 }
955 959
956 960 /**
957 961 * Construct a <code>JSpinner</code> editor that supports displaying
958 962 * and editing the value of a <code>SpinnerDateModel</code>
959 963 * with a <code>JFormattedTextField</code>. <code>This</code>
960 964 * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
961 965 * on the spinners model and a <code>PropertyChangeListener</code>
962 966 * on the new <code>JFormattedTextField</code>.
963 967 *
964 968 * @param spinner the spinner whose model <code>this</code> editor will monitor
965 969 * @exception IllegalArgumentException if the spinners model is not
966 970 * an instance of <code>SpinnerDateModel</code>
967 971 *
968 972 * @see #getModel
969 973 * @see #getFormat
970 974 * @see SpinnerDateModel
971 975 */
972 976 public DateEditor(JSpinner spinner) {
973 977 this(spinner, getDefaultPattern(spinner.getLocale()));
974 978 }
975 979
976 980
977 981 /**
978 982 * Construct a <code>JSpinner</code> editor that supports displaying
979 983 * and editing the value of a <code>SpinnerDateModel</code>
980 984 * with a <code>JFormattedTextField</code>. <code>This</code>
981 985 * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
982 986 * on the spinner and a <code>PropertyChangeListener</code>
983 987 * on the new <code>JFormattedTextField</code>.
984 988 *
985 989 * @param spinner the spinner whose model <code>this</code> editor will monitor
986 990 * @param dateFormatPattern the initial pattern for the
987 991 * <code>SimpleDateFormat</code> object that's used to display
988 992 * and parse the value of the text field.
989 993 * @exception IllegalArgumentException if the spinners model is not
990 994 * an instance of <code>SpinnerDateModel</code>
991 995 *
992 996 * @see #getModel
993 997 * @see #getFormat
994 998 * @see SpinnerDateModel
995 999 * @see java.text.SimpleDateFormat
996 1000 */
997 1001 public DateEditor(JSpinner spinner, String dateFormatPattern) {
998 1002 this(spinner, new SimpleDateFormat(dateFormatPattern,
999 1003 spinner.getLocale()));
1000 1004 }
1001 1005
1002 1006 /**
1003 1007 * Construct a <code>JSpinner</code> editor that supports displaying
1004 1008 * and editing the value of a <code>SpinnerDateModel</code>
1005 1009 * with a <code>JFormattedTextField</code>. <code>This</code>
1006 1010 * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
1007 1011 * on the spinner and a <code>PropertyChangeListener</code>
1008 1012 * on the new <code>JFormattedTextField</code>.
1009 1013 *
1010 1014 * @param spinner the spinner whose model <code>this</code> editor
1011 1015 * will monitor
1012 1016 * @param format <code>DateFormat</code> object that's used to display
1013 1017 * and parse the value of the text field.
1014 1018 * @exception IllegalArgumentException if the spinners model is not
1015 1019 * an instance of <code>SpinnerDateModel</code>
1016 1020 *
1017 1021 * @see #getModel
1018 1022 * @see #getFormat
1019 1023 * @see SpinnerDateModel
1020 1024 * @see java.text.SimpleDateFormat
1021 1025 */
1022 1026 private DateEditor(JSpinner spinner, DateFormat format) {
1023 1027 super(spinner);
1024 1028 if (!(spinner.getModel() instanceof SpinnerDateModel)) {
1025 1029 throw new IllegalArgumentException(
1026 1030 "model not a SpinnerDateModel");
1027 1031 }
1028 1032
1029 1033 SpinnerDateModel model = (SpinnerDateModel)spinner.getModel();
1030 1034 DateFormatter formatter = new DateEditorFormatter(model, format);
1031 1035 DefaultFormatterFactory factory = new DefaultFormatterFactory(
1032 1036 formatter);
1033 1037 JFormattedTextField ftf = getTextField();
1034 1038 ftf.setEditable(true);
1035 1039 ftf.setFormatterFactory(factory);
1036 1040
1037 1041 /* TBD - initializing the column width of the text field
1038 1042 * is imprecise and doing it here is tricky because
1039 1043 * the developer may configure the formatter later.
1040 1044 */
1041 1045 try {
1042 1046 String maxString = formatter.valueToString(model.getStart());
1043 1047 String minString = formatter.valueToString(model.getEnd());
1044 1048 ftf.setColumns(Math.max(maxString.length(),
1045 1049 minString.length()));
1046 1050 }
1047 1051 catch (ParseException e) {
1048 1052 // PENDING: hmuller
1049 1053 }
1050 1054 }
1051 1055
1052 1056 /**
1053 1057 * Returns the <code>java.text.SimpleDateFormat</code> object the
1054 1058 * <code>JFormattedTextField</code> uses to parse and format
1055 1059 * numbers.
1056 1060 *
1057 1061 * @return the value of <code>getTextField().getFormatter().getFormat()</code>.
1058 1062 * @see #getTextField
1059 1063 * @see java.text.SimpleDateFormat
1060 1064 */
1061 1065 public SimpleDateFormat getFormat() {
1062 1066 return (SimpleDateFormat)((DateFormatter)(getTextField().getFormatter())).getFormat();
1063 1067 }
1064 1068
1065 1069
1066 1070 /**
1067 1071 * Return our spinner ancestor's <code>SpinnerDateModel</code>.
1068 1072 *
1069 1073 * @return <code>getSpinner().getModel()</code>
1070 1074 * @see #getSpinner
1071 1075 * @see #getTextField
1072 1076 */
1073 1077 public SpinnerDateModel getModel() {
1074 1078 return (SpinnerDateModel)(getSpinner().getModel());
1075 1079 }
1076 1080 }
1077 1081
1078 1082
1079 1083 /**
1080 1084 * This subclass of javax.swing.NumberFormatter maps the minimum/maximum
1081 1085 * properties to a SpinnerNumberModel and initializes the valueClass
1082 1086 * of the NumberFormatter to match the type of the initial models value.
1083 1087 */
1084 1088 private static class NumberEditorFormatter extends NumberFormatter {
1085 1089 private final SpinnerNumberModel model;
1086 1090
1087 1091 NumberEditorFormatter(SpinnerNumberModel model, NumberFormat format) {
1088 1092 super(format);
1089 1093 this.model = model;
1090 1094 setValueClass(model.getValue().getClass());
1091 1095 }
1092 1096
1093 1097 public void setMinimum(Comparable min) {
1094 1098 model.setMinimum(min);
1095 1099 }
1096 1100
1097 1101 public Comparable getMinimum() {
1098 1102 return model.getMinimum();
1099 1103 }
1100 1104
1101 1105 public void setMaximum(Comparable max) {
1102 1106 model.setMaximum(max);
1103 1107 }
1104 1108
1105 1109 public Comparable getMaximum() {
1106 1110 return model.getMaximum();
1107 1111 }
1108 1112 }
1109 1113
1110 1114
1111 1115
1112 1116 /**
1113 1117 * An editor for a <code>JSpinner</code> whose model is a
1114 1118 * <code>SpinnerNumberModel</code>. The value of the editor is
1115 1119 * displayed with a <code>JFormattedTextField</code> whose format
1116 1120 * is defined by a <code>NumberFormatter</code> instance whose
1117 1121 * <code>minimum</code> and <code>maximum</code> properties
↓ open down ↓ |
154 lines elided |
↑ open up ↑ |
1118 1122 * are mapped to the <code>SpinnerNumberModel</code>.
1119 1123 * @since 1.4
1120 1124 */
1121 1125 // PENDING(hmuller): more example javadoc
1122 1126 public static class NumberEditor extends DefaultEditor
1123 1127 {
1124 1128 // This is here until DecimalFormat gets a constructor that
1125 1129 // takes a Locale: 4923525
1126 1130 private static String getDefaultPattern(Locale locale) {
1127 1131 // Get the pattern for the default locale.
1128 - ResourceBundle rb = LocaleData.getNumberFormatData(locale);
1129 - String[] all = rb.getStringArray("NumberPatterns");
1132 + LocaleProviderAdapter adapter;
1133 + adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class,
1134 + locale);
1135 + LocaleResources lr = adapter.getLocaleResources(locale);
1136 + if (lr == null) {
1137 + lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale);
1138 + }
1139 + String[] all = lr.getNumberPatterns();
1130 1140 return all[0];
1131 1141 }
1132 1142
1133 1143 /**
1134 1144 * Construct a <code>JSpinner</code> editor that supports displaying
1135 1145 * and editing the value of a <code>SpinnerNumberModel</code>
1136 1146 * with a <code>JFormattedTextField</code>. <code>This</code>
1137 1147 * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
1138 1148 * on the spinner and a <code>PropertyChangeListener</code>
1139 1149 * on the new <code>JFormattedTextField</code>.
1140 1150 *
1141 1151 * @param spinner the spinner whose model <code>this</code> editor will monitor
1142 1152 * @exception IllegalArgumentException if the spinners model is not
1143 1153 * an instance of <code>SpinnerNumberModel</code>
1144 1154 *
1145 1155 * @see #getModel
1146 1156 * @see #getFormat
1147 1157 * @see SpinnerNumberModel
1148 1158 */
1149 1159 public NumberEditor(JSpinner spinner) {
1150 1160 this(spinner, getDefaultPattern(spinner.getLocale()));
1151 1161 }
1152 1162
1153 1163 /**
1154 1164 * Construct a <code>JSpinner</code> editor that supports displaying
1155 1165 * and editing the value of a <code>SpinnerNumberModel</code>
1156 1166 * with a <code>JFormattedTextField</code>. <code>This</code>
1157 1167 * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
1158 1168 * on the spinner and a <code>PropertyChangeListener</code>
1159 1169 * on the new <code>JFormattedTextField</code>.
1160 1170 *
1161 1171 * @param spinner the spinner whose model <code>this</code> editor will monitor
1162 1172 * @param decimalFormatPattern the initial pattern for the
1163 1173 * <code>DecimalFormat</code> object that's used to display
1164 1174 * and parse the value of the text field.
1165 1175 * @exception IllegalArgumentException if the spinners model is not
1166 1176 * an instance of <code>SpinnerNumberModel</code> or if
1167 1177 * <code>decimalFormatPattern</code> is not a legal
1168 1178 * argument to <code>DecimalFormat</code>
1169 1179 *
1170 1180 * @see #getTextField
1171 1181 * @see SpinnerNumberModel
1172 1182 * @see java.text.DecimalFormat
1173 1183 */
1174 1184 public NumberEditor(JSpinner spinner, String decimalFormatPattern) {
1175 1185 this(spinner, new DecimalFormat(decimalFormatPattern));
1176 1186 }
1177 1187
1178 1188
1179 1189 /**
1180 1190 * Construct a <code>JSpinner</code> editor that supports displaying
1181 1191 * and editing the value of a <code>SpinnerNumberModel</code>
1182 1192 * with a <code>JFormattedTextField</code>. <code>This</code>
1183 1193 * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
1184 1194 * on the spinner and a <code>PropertyChangeListener</code>
1185 1195 * on the new <code>JFormattedTextField</code>.
1186 1196 *
1187 1197 * @param spinner the spinner whose model <code>this</code> editor will monitor
1188 1198 * @param decimalFormatPattern the initial pattern for the
1189 1199 * <code>DecimalFormat</code> object that's used to display
1190 1200 * and parse the value of the text field.
1191 1201 * @exception IllegalArgumentException if the spinners model is not
1192 1202 * an instance of <code>SpinnerNumberModel</code>
1193 1203 *
1194 1204 * @see #getTextField
1195 1205 * @see SpinnerNumberModel
1196 1206 * @see java.text.DecimalFormat
1197 1207 */
1198 1208 private NumberEditor(JSpinner spinner, DecimalFormat format) {
1199 1209 super(spinner);
1200 1210 if (!(spinner.getModel() instanceof SpinnerNumberModel)) {
1201 1211 throw new IllegalArgumentException(
1202 1212 "model not a SpinnerNumberModel");
1203 1213 }
1204 1214
1205 1215 SpinnerNumberModel model = (SpinnerNumberModel)spinner.getModel();
1206 1216 NumberFormatter formatter = new NumberEditorFormatter(model,
1207 1217 format);
1208 1218 DefaultFormatterFactory factory = new DefaultFormatterFactory(
1209 1219 formatter);
1210 1220 JFormattedTextField ftf = getTextField();
1211 1221 ftf.setEditable(true);
1212 1222 ftf.setFormatterFactory(factory);
1213 1223 ftf.setHorizontalAlignment(JTextField.RIGHT);
1214 1224
1215 1225 /* TBD - initializing the column width of the text field
1216 1226 * is imprecise and doing it here is tricky because
1217 1227 * the developer may configure the formatter later.
1218 1228 */
1219 1229 try {
1220 1230 String maxString = formatter.valueToString(model.getMinimum());
1221 1231 String minString = formatter.valueToString(model.getMaximum());
1222 1232 ftf.setColumns(Math.max(maxString.length(),
1223 1233 minString.length()));
1224 1234 }
1225 1235 catch (ParseException e) {
1226 1236 // TBD should throw a chained error here
1227 1237 }
1228 1238
1229 1239 }
1230 1240
1231 1241
1232 1242 /**
1233 1243 * Returns the <code>java.text.DecimalFormat</code> object the
1234 1244 * <code>JFormattedTextField</code> uses to parse and format
1235 1245 * numbers.
1236 1246 *
1237 1247 * @return the value of <code>getTextField().getFormatter().getFormat()</code>.
1238 1248 * @see #getTextField
1239 1249 * @see java.text.DecimalFormat
1240 1250 */
1241 1251 public DecimalFormat getFormat() {
1242 1252 return (DecimalFormat)((NumberFormatter)(getTextField().getFormatter())).getFormat();
1243 1253 }
1244 1254
1245 1255
1246 1256 /**
1247 1257 * Return our spinner ancestor's <code>SpinnerNumberModel</code>.
1248 1258 *
1249 1259 * @return <code>getSpinner().getModel()</code>
1250 1260 * @see #getSpinner
1251 1261 * @see #getTextField
1252 1262 */
1253 1263 public SpinnerNumberModel getModel() {
1254 1264 return (SpinnerNumberModel)(getSpinner().getModel());
1255 1265 }
1256 1266 }
1257 1267
1258 1268
1259 1269 /**
1260 1270 * An editor for a <code>JSpinner</code> whose model is a
1261 1271 * <code>SpinnerListModel</code>.
1262 1272 * @since 1.4
1263 1273 */
1264 1274 public static class ListEditor extends DefaultEditor
1265 1275 {
1266 1276 /**
1267 1277 * Construct a <code>JSpinner</code> editor that supports displaying
1268 1278 * and editing the value of a <code>SpinnerListModel</code>
1269 1279 * with a <code>JFormattedTextField</code>. <code>This</code>
1270 1280 * <code>ListEditor</code> becomes both a <code>ChangeListener</code>
1271 1281 * on the spinner and a <code>PropertyChangeListener</code>
1272 1282 * on the new <code>JFormattedTextField</code>.
1273 1283 *
1274 1284 * @param spinner the spinner whose model <code>this</code> editor will monitor
1275 1285 * @exception IllegalArgumentException if the spinners model is not
1276 1286 * an instance of <code>SpinnerListModel</code>
1277 1287 *
1278 1288 * @see #getModel
1279 1289 * @see SpinnerListModel
1280 1290 */
1281 1291 public ListEditor(JSpinner spinner) {
1282 1292 super(spinner);
1283 1293 if (!(spinner.getModel() instanceof SpinnerListModel)) {
1284 1294 throw new IllegalArgumentException("model not a SpinnerListModel");
1285 1295 }
1286 1296 getTextField().setEditable(true);
1287 1297 getTextField().setFormatterFactory(new
1288 1298 DefaultFormatterFactory(new ListFormatter()));
1289 1299 }
1290 1300
1291 1301 /**
1292 1302 * Return our spinner ancestor's <code>SpinnerNumberModel</code>.
1293 1303 *
1294 1304 * @return <code>getSpinner().getModel()</code>
1295 1305 * @see #getSpinner
1296 1306 * @see #getTextField
1297 1307 */
1298 1308 public SpinnerListModel getModel() {
1299 1309 return (SpinnerListModel)(getSpinner().getModel());
1300 1310 }
1301 1311
1302 1312
1303 1313 /**
1304 1314 * ListFormatter provides completion while text is being input
1305 1315 * into the JFormattedTextField. Completion is only done if the
1306 1316 * user is inserting text at the end of the document. Completion
1307 1317 * is done by way of the SpinnerListModel method findNextMatch.
1308 1318 */
1309 1319 private class ListFormatter extends
1310 1320 JFormattedTextField.AbstractFormatter {
1311 1321 private DocumentFilter filter;
1312 1322
1313 1323 public String valueToString(Object value) throws ParseException {
1314 1324 if (value == null) {
1315 1325 return "";
1316 1326 }
1317 1327 return value.toString();
1318 1328 }
1319 1329
1320 1330 public Object stringToValue(String string) throws ParseException {
1321 1331 return string;
1322 1332 }
1323 1333
1324 1334 protected DocumentFilter getDocumentFilter() {
1325 1335 if (filter == null) {
1326 1336 filter = new Filter();
1327 1337 }
1328 1338 return filter;
1329 1339 }
1330 1340
1331 1341
1332 1342 private class Filter extends DocumentFilter {
1333 1343 public void replace(FilterBypass fb, int offset, int length,
1334 1344 String string, AttributeSet attrs) throws
1335 1345 BadLocationException {
1336 1346 if (string != null && (offset + length) ==
1337 1347 fb.getDocument().getLength()) {
1338 1348 Object next = getModel().findNextMatch(
1339 1349 fb.getDocument().getText(0, offset) +
1340 1350 string);
1341 1351 String value = (next != null) ? next.toString() : null;
1342 1352
1343 1353 if (value != null) {
1344 1354 fb.remove(0, offset + length);
1345 1355 fb.insertString(0, value, null);
1346 1356 getFormattedTextField().select(offset +
1347 1357 string.length(),
1348 1358 value.length());
1349 1359 return;
1350 1360 }
1351 1361 }
1352 1362 super.replace(fb, offset, length, string, attrs);
1353 1363 }
1354 1364
1355 1365 public void insertString(FilterBypass fb, int offset,
1356 1366 String string, AttributeSet attr)
1357 1367 throws BadLocationException {
1358 1368 replace(fb, offset, 0, string, attr);
1359 1369 }
1360 1370 }
1361 1371 }
1362 1372 }
1363 1373
1364 1374
1365 1375 /**
1366 1376 * An Action implementation that is always disabled.
1367 1377 */
1368 1378 private static class DisabledAction implements Action {
1369 1379 public Object getValue(String key) {
1370 1380 return null;
1371 1381 }
1372 1382 public void putValue(String key, Object value) {
1373 1383 }
1374 1384 public void setEnabled(boolean b) {
1375 1385 }
1376 1386 public boolean isEnabled() {
1377 1387 return false;
1378 1388 }
1379 1389 public void addPropertyChangeListener(PropertyChangeListener l) {
1380 1390 }
1381 1391 public void removePropertyChangeListener(PropertyChangeListener l) {
1382 1392 }
1383 1393 public void actionPerformed(ActionEvent ae) {
1384 1394 }
1385 1395 }
1386 1396
1387 1397 /////////////////
1388 1398 // Accessibility support
1389 1399 ////////////////
1390 1400
1391 1401 /**
1392 1402 * Gets the <code>AccessibleContext</code> for the <code>JSpinner</code>
1393 1403 *
1394 1404 * @return the <code>AccessibleContext</code> for the <code>JSpinner</code>
1395 1405 * @since 1.5
1396 1406 */
1397 1407 public AccessibleContext getAccessibleContext() {
1398 1408 if (accessibleContext == null) {
1399 1409 accessibleContext = new AccessibleJSpinner();
1400 1410 }
1401 1411 return accessibleContext;
1402 1412 }
1403 1413
1404 1414 /**
1405 1415 * <code>AccessibleJSpinner</code> implements accessibility
1406 1416 * support for the <code>JSpinner</code> class.
1407 1417 * @since 1.5
1408 1418 */
1409 1419 protected class AccessibleJSpinner extends AccessibleJComponent
1410 1420 implements AccessibleValue, AccessibleAction, AccessibleText,
1411 1421 AccessibleEditableText, ChangeListener {
1412 1422
1413 1423 private Object oldModelValue = null;
1414 1424
1415 1425 /**
1416 1426 * AccessibleJSpinner constructor
1417 1427 */
1418 1428 protected AccessibleJSpinner() {
1419 1429 // model is guaranteed to be non-null
1420 1430 oldModelValue = model.getValue();
1421 1431 JSpinner.this.addChangeListener(this);
1422 1432 }
1423 1433
1424 1434 /**
1425 1435 * Invoked when the target of the listener has changed its state.
1426 1436 *
1427 1437 * @param e a <code>ChangeEvent</code> object. Must not be null.
1428 1438 * @throws NullPointerException if the parameter is null.
1429 1439 */
1430 1440 public void stateChanged(ChangeEvent e) {
1431 1441 if (e == null) {
1432 1442 throw new NullPointerException();
1433 1443 }
1434 1444 Object newModelValue = model.getValue();
1435 1445 firePropertyChange(ACCESSIBLE_VALUE_PROPERTY,
1436 1446 oldModelValue,
1437 1447 newModelValue);
1438 1448 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
1439 1449 null,
1440 1450 0); // entire text may have changed
1441 1451
1442 1452 oldModelValue = newModelValue;
1443 1453 }
1444 1454
1445 1455 /* ===== Begin AccessibleContext methods ===== */
1446 1456
1447 1457 /**
1448 1458 * Gets the role of this object. The role of the object is the generic
1449 1459 * purpose or use of the class of this object. For example, the role
1450 1460 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
1451 1461 * AccessibleRole are provided so component developers can pick from
1452 1462 * a set of predefined roles. This enables assistive technologies to
1453 1463 * provide a consistent interface to various tweaked subclasses of
1454 1464 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
1455 1465 * that act like a push button) as well as distinguish between sublasses
1456 1466 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
1457 1467 * and AccessibleRole.RADIO_BUTTON for radio buttons).
1458 1468 * <p>Note that the AccessibleRole class is also extensible, so
1459 1469 * custom component developers can define their own AccessibleRole's
1460 1470 * if the set of predefined roles is inadequate.
1461 1471 *
1462 1472 * @return an instance of AccessibleRole describing the role of the object
1463 1473 * @see AccessibleRole
1464 1474 */
1465 1475 public AccessibleRole getAccessibleRole() {
1466 1476 return AccessibleRole.SPIN_BOX;
1467 1477 }
1468 1478
1469 1479 /**
1470 1480 * Returns the number of accessible children of the object.
1471 1481 *
1472 1482 * @return the number of accessible children of the object.
1473 1483 */
1474 1484 public int getAccessibleChildrenCount() {
1475 1485 // the JSpinner has one child, the editor
1476 1486 if (editor.getAccessibleContext() != null) {
1477 1487 return 1;
1478 1488 }
1479 1489 return 0;
1480 1490 }
1481 1491
1482 1492 /**
1483 1493 * Returns the specified Accessible child of the object. The Accessible
1484 1494 * children of an Accessible object are zero-based, so the first child
1485 1495 * of an Accessible child is at index 0, the second child is at index 1,
1486 1496 * and so on.
1487 1497 *
1488 1498 * @param i zero-based index of child
1489 1499 * @return the Accessible child of the object
1490 1500 * @see #getAccessibleChildrenCount
1491 1501 */
1492 1502 public Accessible getAccessibleChild(int i) {
1493 1503 // the JSpinner has one child, the editor
1494 1504 if (i != 0) {
1495 1505 return null;
1496 1506 }
1497 1507 if (editor.getAccessibleContext() != null) {
1498 1508 return (Accessible)editor;
1499 1509 }
1500 1510 return null;
1501 1511 }
1502 1512
1503 1513 /* ===== End AccessibleContext methods ===== */
1504 1514
1505 1515 /**
1506 1516 * Gets the AccessibleAction associated with this object that supports
1507 1517 * one or more actions.
1508 1518 *
1509 1519 * @return AccessibleAction if supported by object; else return null
1510 1520 * @see AccessibleAction
1511 1521 */
1512 1522 public AccessibleAction getAccessibleAction() {
1513 1523 return this;
1514 1524 }
1515 1525
1516 1526 /**
1517 1527 * Gets the AccessibleText associated with this object presenting
1518 1528 * text on the display.
1519 1529 *
1520 1530 * @return AccessibleText if supported by object; else return null
1521 1531 * @see AccessibleText
1522 1532 */
1523 1533 public AccessibleText getAccessibleText() {
1524 1534 return this;
1525 1535 }
1526 1536
1527 1537 /*
1528 1538 * Returns the AccessibleContext for the JSpinner editor
1529 1539 */
1530 1540 private AccessibleContext getEditorAccessibleContext() {
1531 1541 if (editor instanceof DefaultEditor) {
1532 1542 JTextField textField = ((DefaultEditor)editor).getTextField();
1533 1543 if (textField != null) {
1534 1544 return textField.getAccessibleContext();
1535 1545 }
1536 1546 } else if (editor instanceof Accessible) {
1537 1547 return editor.getAccessibleContext();
1538 1548 }
1539 1549 return null;
1540 1550 }
1541 1551
1542 1552 /*
1543 1553 * Returns the AccessibleText for the JSpinner editor
1544 1554 */
1545 1555 private AccessibleText getEditorAccessibleText() {
1546 1556 AccessibleContext ac = getEditorAccessibleContext();
1547 1557 if (ac != null) {
1548 1558 return ac.getAccessibleText();
1549 1559 }
1550 1560 return null;
1551 1561 }
1552 1562
1553 1563 /*
1554 1564 * Returns the AccessibleEditableText for the JSpinner editor
1555 1565 */
1556 1566 private AccessibleEditableText getEditorAccessibleEditableText() {
1557 1567 AccessibleText at = getEditorAccessibleText();
1558 1568 if (at instanceof AccessibleEditableText) {
1559 1569 return (AccessibleEditableText)at;
1560 1570 }
1561 1571 return null;
1562 1572 }
1563 1573
1564 1574 /**
1565 1575 * Gets the AccessibleValue associated with this object.
1566 1576 *
1567 1577 * @return AccessibleValue if supported by object; else return null
1568 1578 * @see AccessibleValue
1569 1579 *
1570 1580 */
1571 1581 public AccessibleValue getAccessibleValue() {
1572 1582 return this;
1573 1583 }
1574 1584
1575 1585 /* ===== Begin AccessibleValue impl ===== */
1576 1586
1577 1587 /**
1578 1588 * Get the value of this object as a Number. If the value has not been
1579 1589 * set, the return value will be null.
1580 1590 *
1581 1591 * @return value of the object
1582 1592 * @see #setCurrentAccessibleValue
1583 1593 */
1584 1594 public Number getCurrentAccessibleValue() {
1585 1595 Object o = model.getValue();
1586 1596 if (o instanceof Number) {
1587 1597 return (Number)o;
1588 1598 }
1589 1599 return null;
1590 1600 }
1591 1601
1592 1602 /**
1593 1603 * Set the value of this object as a Number.
1594 1604 *
1595 1605 * @param n the value to set for this object
1596 1606 * @return true if the value was set; else False
1597 1607 * @see #getCurrentAccessibleValue
1598 1608 */
1599 1609 public boolean setCurrentAccessibleValue(Number n) {
1600 1610 // try to set the new value
1601 1611 try {
1602 1612 model.setValue(n);
1603 1613 return true;
1604 1614 } catch (IllegalArgumentException iae) {
1605 1615 // SpinnerModel didn't like new value
1606 1616 }
1607 1617 return false;
1608 1618 }
1609 1619
1610 1620 /**
1611 1621 * Get the minimum value of this object as a Number.
1612 1622 *
1613 1623 * @return Minimum value of the object; null if this object does not
1614 1624 * have a minimum value
1615 1625 * @see #getMaximumAccessibleValue
1616 1626 */
1617 1627 public Number getMinimumAccessibleValue() {
1618 1628 if (model instanceof SpinnerNumberModel) {
1619 1629 SpinnerNumberModel numberModel = (SpinnerNumberModel)model;
1620 1630 Object o = numberModel.getMinimum();
1621 1631 if (o instanceof Number) {
1622 1632 return (Number)o;
1623 1633 }
1624 1634 }
1625 1635 return null;
1626 1636 }
1627 1637
1628 1638 /**
1629 1639 * Get the maximum value of this object as a Number.
1630 1640 *
1631 1641 * @return Maximum value of the object; null if this object does not
1632 1642 * have a maximum value
1633 1643 * @see #getMinimumAccessibleValue
1634 1644 */
1635 1645 public Number getMaximumAccessibleValue() {
1636 1646 if (model instanceof SpinnerNumberModel) {
1637 1647 SpinnerNumberModel numberModel = (SpinnerNumberModel)model;
1638 1648 Object o = numberModel.getMaximum();
1639 1649 if (o instanceof Number) {
1640 1650 return (Number)o;
1641 1651 }
1642 1652 }
1643 1653 return null;
1644 1654 }
1645 1655
1646 1656 /* ===== End AccessibleValue impl ===== */
1647 1657
1648 1658 /* ===== Begin AccessibleAction impl ===== */
1649 1659
1650 1660 /**
1651 1661 * Returns the number of accessible actions available in this object
1652 1662 * If there are more than one, the first one is considered the "default"
1653 1663 * action of the object.
1654 1664 *
1655 1665 * Two actions are supported: AccessibleAction.INCREMENT which
1656 1666 * increments the spinner value and AccessibleAction.DECREMENT
1657 1667 * which decrements the spinner value
1658 1668 *
1659 1669 * @return the zero-based number of Actions in this object
1660 1670 */
1661 1671 public int getAccessibleActionCount() {
1662 1672 return 2;
1663 1673 }
1664 1674
1665 1675 /**
1666 1676 * Returns a description of the specified action of the object.
1667 1677 *
1668 1678 * @param i zero-based index of the actions
1669 1679 * @return a String description of the action
1670 1680 * @see #getAccessibleActionCount
1671 1681 */
1672 1682 public String getAccessibleActionDescription(int i) {
1673 1683 if (i == 0) {
1674 1684 return AccessibleAction.INCREMENT;
1675 1685 } else if (i == 1) {
1676 1686 return AccessibleAction.DECREMENT;
1677 1687 }
1678 1688 return null;
1679 1689 }
1680 1690
1681 1691 /**
1682 1692 * Performs the specified Action on the object
1683 1693 *
1684 1694 * @param i zero-based index of actions. The first action
1685 1695 * (index 0) is AccessibleAction.INCREMENT and the second
1686 1696 * action (index 1) is AccessibleAction.DECREMENT.
1687 1697 * @return true if the action was performed; otherwise false.
1688 1698 * @see #getAccessibleActionCount
1689 1699 */
1690 1700 public boolean doAccessibleAction(int i) {
1691 1701 if (i < 0 || i > 1) {
1692 1702 return false;
1693 1703 }
1694 1704 Object o;
1695 1705 if (i == 0) {
1696 1706 o = getNextValue(); // AccessibleAction.INCREMENT
1697 1707 } else {
1698 1708 o = getPreviousValue(); // AccessibleAction.DECREMENT
1699 1709 }
1700 1710 // try to set the new value
1701 1711 try {
1702 1712 model.setValue(o);
1703 1713 return true;
1704 1714 } catch (IllegalArgumentException iae) {
1705 1715 // SpinnerModel didn't like new value
1706 1716 }
1707 1717 return false;
1708 1718 }
1709 1719
1710 1720 /* ===== End AccessibleAction impl ===== */
1711 1721
1712 1722 /* ===== Begin AccessibleText impl ===== */
1713 1723
1714 1724 /*
1715 1725 * Returns whether source and destination components have the
1716 1726 * same window ancestor
1717 1727 */
1718 1728 private boolean sameWindowAncestor(Component src, Component dest) {
1719 1729 if (src == null || dest == null) {
1720 1730 return false;
1721 1731 }
1722 1732 return SwingUtilities.getWindowAncestor(src) ==
1723 1733 SwingUtilities.getWindowAncestor(dest);
1724 1734 }
1725 1735
1726 1736 /**
1727 1737 * Given a point in local coordinates, return the zero-based index
1728 1738 * of the character under that Point. If the point is invalid,
1729 1739 * this method returns -1.
1730 1740 *
1731 1741 * @param p the Point in local coordinates
1732 1742 * @return the zero-based index of the character under Point p; if
1733 1743 * Point is invalid return -1.
1734 1744 */
1735 1745 public int getIndexAtPoint(Point p) {
1736 1746 AccessibleText at = getEditorAccessibleText();
1737 1747 if (at != null && sameWindowAncestor(JSpinner.this, editor)) {
1738 1748 // convert point from the JSpinner bounds (source) to
1739 1749 // editor bounds (destination)
1740 1750 Point editorPoint = SwingUtilities.convertPoint(JSpinner.this,
1741 1751 p,
1742 1752 editor);
1743 1753 if (editorPoint != null) {
1744 1754 return at.getIndexAtPoint(editorPoint);
1745 1755 }
1746 1756 }
1747 1757 return -1;
1748 1758 }
1749 1759
1750 1760 /**
1751 1761 * Determines the bounding box of the character at the given
1752 1762 * index into the string. The bounds are returned in local
1753 1763 * coordinates. If the index is invalid an empty rectangle is
1754 1764 * returned.
1755 1765 *
1756 1766 * @param i the index into the String
1757 1767 * @return the screen coordinates of the character's bounding box,
1758 1768 * if index is invalid return an empty rectangle.
1759 1769 */
1760 1770 public Rectangle getCharacterBounds(int i) {
1761 1771 AccessibleText at = getEditorAccessibleText();
1762 1772 if (at != null ) {
1763 1773 Rectangle editorRect = at.getCharacterBounds(i);
1764 1774 if (editorRect != null &&
1765 1775 sameWindowAncestor(JSpinner.this, editor)) {
1766 1776 // return rectangle in the the JSpinner bounds
1767 1777 return SwingUtilities.convertRectangle(editor,
1768 1778 editorRect,
1769 1779 JSpinner.this);
1770 1780 }
1771 1781 }
1772 1782 return null;
1773 1783 }
1774 1784
1775 1785 /**
1776 1786 * Returns the number of characters (valid indicies)
1777 1787 *
1778 1788 * @return the number of characters
1779 1789 */
1780 1790 public int getCharCount() {
1781 1791 AccessibleText at = getEditorAccessibleText();
1782 1792 if (at != null) {
1783 1793 return at.getCharCount();
1784 1794 }
1785 1795 return -1;
1786 1796 }
1787 1797
1788 1798 /**
1789 1799 * Returns the zero-based offset of the caret.
1790 1800 *
1791 1801 * Note: That to the right of the caret will have the same index
1792 1802 * value as the offset (the caret is between two characters).
1793 1803 * @return the zero-based offset of the caret.
1794 1804 */
1795 1805 public int getCaretPosition() {
1796 1806 AccessibleText at = getEditorAccessibleText();
1797 1807 if (at != null) {
1798 1808 return at.getCaretPosition();
1799 1809 }
1800 1810 return -1;
1801 1811 }
1802 1812
1803 1813 /**
1804 1814 * Returns the String at a given index.
1805 1815 *
1806 1816 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
1807 1817 * @param index an index within the text
1808 1818 * @return the letter, word, or sentence
1809 1819 */
1810 1820 public String getAtIndex(int part, int index) {
1811 1821 AccessibleText at = getEditorAccessibleText();
1812 1822 if (at != null) {
1813 1823 return at.getAtIndex(part, index);
1814 1824 }
1815 1825 return null;
1816 1826 }
1817 1827
1818 1828 /**
1819 1829 * Returns the String after a given index.
1820 1830 *
1821 1831 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
1822 1832 * @param index an index within the text
1823 1833 * @return the letter, word, or sentence
1824 1834 */
1825 1835 public String getAfterIndex(int part, int index) {
1826 1836 AccessibleText at = getEditorAccessibleText();
1827 1837 if (at != null) {
1828 1838 return at.getAfterIndex(part, index);
1829 1839 }
1830 1840 return null;
1831 1841 }
1832 1842
1833 1843 /**
1834 1844 * Returns the String before a given index.
1835 1845 *
1836 1846 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
1837 1847 * @param index an index within the text
1838 1848 * @return the letter, word, or sentence
1839 1849 */
1840 1850 public String getBeforeIndex(int part, int index) {
1841 1851 AccessibleText at = getEditorAccessibleText();
1842 1852 if (at != null) {
1843 1853 return at.getBeforeIndex(part, index);
1844 1854 }
1845 1855 return null;
1846 1856 }
1847 1857
1848 1858 /**
1849 1859 * Returns the AttributeSet for a given character at a given index
1850 1860 *
1851 1861 * @param i the zero-based index into the text
1852 1862 * @return the AttributeSet of the character
1853 1863 */
1854 1864 public AttributeSet getCharacterAttribute(int i) {
1855 1865 AccessibleText at = getEditorAccessibleText();
1856 1866 if (at != null) {
1857 1867 return at.getCharacterAttribute(i);
1858 1868 }
1859 1869 return null;
1860 1870 }
1861 1871
1862 1872 /**
1863 1873 * Returns the start offset within the selected text.
1864 1874 * If there is no selection, but there is
1865 1875 * a caret, the start and end offsets will be the same.
1866 1876 *
1867 1877 * @return the index into the text of the start of the selection
1868 1878 */
1869 1879 public int getSelectionStart() {
1870 1880 AccessibleText at = getEditorAccessibleText();
1871 1881 if (at != null) {
1872 1882 return at.getSelectionStart();
1873 1883 }
1874 1884 return -1;
1875 1885 }
1876 1886
1877 1887 /**
1878 1888 * Returns the end offset within the selected text.
1879 1889 * If there is no selection, but there is
1880 1890 * a caret, the start and end offsets will be the same.
1881 1891 *
1882 1892 * @return the index into teh text of the end of the selection
1883 1893 */
1884 1894 public int getSelectionEnd() {
1885 1895 AccessibleText at = getEditorAccessibleText();
1886 1896 if (at != null) {
1887 1897 return at.getSelectionEnd();
1888 1898 }
1889 1899 return -1;
1890 1900 }
1891 1901
1892 1902 /**
1893 1903 * Returns the portion of the text that is selected.
1894 1904 *
1895 1905 * @return the String portion of the text that is selected
1896 1906 */
1897 1907 public String getSelectedText() {
1898 1908 AccessibleText at = getEditorAccessibleText();
1899 1909 if (at != null) {
1900 1910 return at.getSelectedText();
1901 1911 }
1902 1912 return null;
1903 1913 }
1904 1914
1905 1915 /* ===== End AccessibleText impl ===== */
1906 1916
1907 1917
1908 1918 /* ===== Begin AccessibleEditableText impl ===== */
1909 1919
1910 1920 /**
1911 1921 * Sets the text contents to the specified string.
1912 1922 *
1913 1923 * @param s the string to set the text contents
1914 1924 */
1915 1925 public void setTextContents(String s) {
1916 1926 AccessibleEditableText at = getEditorAccessibleEditableText();
1917 1927 if (at != null) {
1918 1928 at.setTextContents(s);
1919 1929 }
1920 1930 }
1921 1931
1922 1932 /**
1923 1933 * Inserts the specified string at the given index/
1924 1934 *
1925 1935 * @param index the index in the text where the string will
1926 1936 * be inserted
1927 1937 * @param s the string to insert in the text
1928 1938 */
1929 1939 public void insertTextAtIndex(int index, String s) {
1930 1940 AccessibleEditableText at = getEditorAccessibleEditableText();
1931 1941 if (at != null) {
1932 1942 at.insertTextAtIndex(index, s);
1933 1943 }
1934 1944 }
1935 1945
1936 1946 /**
1937 1947 * Returns the text string between two indices.
1938 1948 *
1939 1949 * @param startIndex the starting index in the text
1940 1950 * @param endIndex the ending index in the text
1941 1951 * @return the text string between the indices
1942 1952 */
1943 1953 public String getTextRange(int startIndex, int endIndex) {
1944 1954 AccessibleEditableText at = getEditorAccessibleEditableText();
1945 1955 if (at != null) {
1946 1956 return at.getTextRange(startIndex, endIndex);
1947 1957 }
1948 1958 return null;
1949 1959 }
1950 1960
1951 1961 /**
1952 1962 * Deletes the text between two indices
1953 1963 *
1954 1964 * @param startIndex the starting index in the text
1955 1965 * @param endIndex the ending index in the text
1956 1966 */
1957 1967 public void delete(int startIndex, int endIndex) {
1958 1968 AccessibleEditableText at = getEditorAccessibleEditableText();
1959 1969 if (at != null) {
1960 1970 at.delete(startIndex, endIndex);
1961 1971 }
1962 1972 }
1963 1973
1964 1974 /**
1965 1975 * Cuts the text between two indices into the system clipboard.
1966 1976 *
1967 1977 * @param startIndex the starting index in the text
1968 1978 * @param endIndex the ending index in the text
1969 1979 */
1970 1980 public void cut(int startIndex, int endIndex) {
1971 1981 AccessibleEditableText at = getEditorAccessibleEditableText();
1972 1982 if (at != null) {
1973 1983 at.cut(startIndex, endIndex);
1974 1984 }
1975 1985 }
1976 1986
1977 1987 /**
1978 1988 * Pastes the text from the system clipboard into the text
1979 1989 * starting at the specified index.
1980 1990 *
1981 1991 * @param startIndex the starting index in the text
1982 1992 */
1983 1993 public void paste(int startIndex) {
1984 1994 AccessibleEditableText at = getEditorAccessibleEditableText();
1985 1995 if (at != null) {
1986 1996 at.paste(startIndex);
1987 1997 }
1988 1998 }
1989 1999
1990 2000 /**
1991 2001 * Replaces the text between two indices with the specified
1992 2002 * string.
1993 2003 *
1994 2004 * @param startIndex the starting index in the text
1995 2005 * @param endIndex the ending index in the text
1996 2006 * @param s the string to replace the text between two indices
1997 2007 */
1998 2008 public void replaceText(int startIndex, int endIndex, String s) {
1999 2009 AccessibleEditableText at = getEditorAccessibleEditableText();
2000 2010 if (at != null) {
2001 2011 at.replaceText(startIndex, endIndex, s);
2002 2012 }
2003 2013 }
2004 2014
2005 2015 /**
2006 2016 * Selects the text between two indices.
2007 2017 *
2008 2018 * @param startIndex the starting index in the text
2009 2019 * @param endIndex the ending index in the text
2010 2020 */
2011 2021 public void selectText(int startIndex, int endIndex) {
2012 2022 AccessibleEditableText at = getEditorAccessibleEditableText();
2013 2023 if (at != null) {
2014 2024 at.selectText(startIndex, endIndex);
2015 2025 }
2016 2026 }
2017 2027
2018 2028 /**
2019 2029 * Sets attributes for the text between two indices.
2020 2030 *
2021 2031 * @param startIndex the starting index in the text
2022 2032 * @param endIndex the ending index in the text
2023 2033 * @param as the attribute set
2024 2034 * @see AttributeSet
2025 2035 */
2026 2036 public void setAttributes(int startIndex, int endIndex, AttributeSet as) {
2027 2037 AccessibleEditableText at = getEditorAccessibleEditableText();
2028 2038 if (at != null) {
2029 2039 at.setAttributes(startIndex, endIndex, as);
2030 2040 }
2031 2041 }
2032 2042 } /* End AccessibleJSpinner */
2033 2043 }
↓ open down ↓ |
894 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX