GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* JSpinner.java -- 2: Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.Component; 42: import java.awt.Container; 43: import java.awt.Dimension; 44: import java.awt.Insets; 45: import java.awt.LayoutManager; 46: import java.beans.PropertyChangeEvent; 47: import java.beans.PropertyChangeListener; 48: import java.text.DecimalFormat; 49: import java.text.ParseException; 50: import java.text.SimpleDateFormat; 51: 52: import javax.swing.event.ChangeEvent; 53: import javax.swing.event.ChangeListener; 54: import javax.swing.plaf.SpinnerUI; 55: import javax.swing.text.DateFormatter; 56: 57: /** 58: * A JSpinner is a component which typically contains a numeric value and a 59: * way to manipulate the value. 60: * 61: * @author Ka-Hing Cheung 62: * 63: * @since 1.4 64: */ 65: public class JSpinner extends JComponent 66: { 67: /** 68: * DOCUMENT ME! 69: */ 70: public static class DefaultEditor extends JPanel implements ChangeListener, 71: PropertyChangeListener, 72: LayoutManager 73: { 74: private JSpinner spinner; 75: 76: /** The JFormattedTextField that backs the editor. */ 77: JFormattedTextField ftf; 78: 79: /** 80: * For compatability with Sun's JDK 1.4.2 rev. 5 81: */ 82: private static final long serialVersionUID = -5317788736173368172L; 83: 84: /** 85: * Creates a new <code>DefaultEditor</code> object. 86: * 87: * @param spinner the <code>JSpinner</code> associated with this editor 88: */ 89: public DefaultEditor(JSpinner spinner) 90: { 91: super(); 92: setLayout(this); 93: this.spinner = spinner; 94: ftf = new JFormattedTextField(); 95: add(ftf); 96: ftf.setValue(spinner.getValue()); 97: spinner.addChangeListener(this); 98: } 99: 100: /** 101: * Returns the <code>JSpinner</code> object for this editor. 102: */ 103: public JSpinner getSpinner() 104: { 105: return spinner; 106: } 107: 108: /** 109: * DOCUMENT ME! 110: */ 111: public void commitEdit() 112: throws ParseException 113: { 114: } /* TODO */ 115: 116: /** 117: * DOCUMENT ME! 118: * 119: * @param spinner DOCUMENT ME! 120: */ 121: public void dismiss(JSpinner spinner) 122: { 123: spinner.removeChangeListener(this); 124: } 125: 126: /** 127: * DOCUMENT ME! 128: * 129: * @return DOCUMENT ME! 130: */ 131: public JFormattedTextField getTextField() 132: { 133: return ftf; 134: } 135: 136: /** 137: * DOCUMENT ME! 138: * 139: * @param parent DOCUMENT ME! 140: */ 141: public void layoutContainer(Container parent) 142: { 143: Insets insets = getInsets(); 144: Dimension size = getSize(); 145: ftf.setBounds(insets.left, insets.top, 146: size.width - insets.left - insets.right, 147: size.height - insets.top - insets.bottom); 148: } 149: 150: /** 151: * DOCUMENT ME! 152: * 153: * @param parent DOCUMENT ME! 154: * 155: * @return DOCUMENT ME! 156: */ 157: public Dimension minimumLayoutSize(Container parent) 158: { 159: Insets insets = getInsets(); 160: Dimension minSize = ftf.getMinimumSize(); 161: return new Dimension(minSize.width + insets.left + insets.right, 162: minSize.height + insets.top + insets.bottom); 163: } 164: 165: /** 166: * DOCUMENT ME! 167: * 168: * @param parent DOCUMENT ME! 169: * 170: * @return DOCUMENT ME! 171: */ 172: public Dimension preferredLayoutSize(Container parent) 173: { 174: Insets insets = getInsets(); 175: Dimension prefSize = ftf.getPreferredSize(); 176: return new Dimension(prefSize.width + insets.left + insets.right, 177: prefSize.height + insets.top + insets.bottom); 178: } 179: 180: /** 181: * DOCUMENT ME! 182: * 183: * @param event DOCUMENT ME! 184: */ 185: public void propertyChange(PropertyChangeEvent event) 186: { 187: } /* TODO */ 188: 189: /** 190: * DOCUMENT ME! 191: * 192: * @param event DOCUMENT ME! 193: */ 194: public void stateChanged(ChangeEvent event) 195: { 196: } /* TODO */ 197: 198: /* no-ops */ 199: public void removeLayoutComponent(Component child) 200: { 201: } 202: 203: /** 204: * DOCUMENT ME! 205: * 206: * @param name DOCUMENT ME! 207: * @param child DOCUMENT ME! 208: */ 209: public void addLayoutComponent(String name, Component child) 210: { 211: } 212: } 213: 214: /** 215: * DOCUMENT ME! 216: */ 217: public static class NumberEditor extends DefaultEditor 218: { 219: /** 220: * For compatability with Sun's JDK 221: */ 222: private static final long serialVersionUID = 3791956183098282942L; 223: 224: /** 225: * Creates a new NumberEditor object. 226: * 227: * @param spinner DOCUMENT ME! 228: */ 229: public NumberEditor(JSpinner spinner) 230: { 231: super(spinner); 232: } 233: 234: /** 235: * Creates a new NumberEditor object. 236: * 237: * @param spinner DOCUMENT ME! 238: */ 239: public NumberEditor(JSpinner spinner, String decimalFormatPattern) 240: { 241: super(spinner); 242: } 243: 244: /** 245: * DOCUMENT ME! 246: * 247: * @return DOCUMENT ME! 248: */ 249: public DecimalFormat getFormat() 250: { 251: return null; 252: } 253: 254: public SpinnerNumberModel getModel() 255: { 256: return (SpinnerNumberModel) getSpinner().getModel(); 257: } 258: } 259: 260: /** 261: * An editor class for a <code>JSpinner</code> that is used 262: * for displaying and editing dates (e.g. that uses 263: * <code>SpinnerDateModel</code> as model). 264: * 265: * The editor uses a {@link JTextField} with the value 266: * displayed by a {@link DateFormatter} instance. 267: */ 268: public static class DateEditor extends DefaultEditor 269: { 270: 271: /** The serialVersionUID. */ 272: private static final long serialVersionUID = -4279356973770397815L; 273: 274: /** The DateFormat instance used to format the date. */ 275: SimpleDateFormat dateFormat; 276: 277: /** 278: * Creates a new instance of DateEditor for the specified 279: * <code>JSpinner</code>. 280: * 281: * @param spinner the <code>JSpinner</code> for which to 282: * create a <code>DateEditor</code> instance 283: */ 284: public DateEditor(JSpinner spinner) 285: { 286: super(spinner); 287: init(new SimpleDateFormat()); 288: } 289: 290: /** 291: * Creates a new instance of DateEditor for the specified 292: * <code>JSpinner</code> using the specified date format 293: * pattern. 294: * 295: * @param spinner the <code>JSpinner</code> for which to 296: * create a <code>DateEditor</code> instance 297: * @param dateFormatPattern the date format to use 298: * 299: * @see SimpleDateFormat#SimpleDateFormat(String) 300: */ 301: public DateEditor(JSpinner spinner, String dateFormatPattern) 302: { 303: super(spinner); 304: init(new SimpleDateFormat(dateFormatPattern)); 305: } 306: 307: /** 308: * Initializes the JFormattedTextField for this editor. 309: * 310: * @param the date format to use in the formatted text field 311: */ 312: private void init(SimpleDateFormat format) 313: { 314: dateFormat = format; 315: getTextField().setFormatterFactory( 316: new JFormattedTextField.AbstractFormatterFactory() 317: { 318: public JFormattedTextField.AbstractFormatter 319: getFormatter(JFormattedTextField ftf) 320: { 321: return new DateFormatter(dateFormat); 322: } 323: }); 324: } 325: 326: /** 327: * Returns the <code>SimpleDateFormat</code> instance that is used to 328: * format the date value. 329: * 330: * @return the <code>SimpleDateFormat</code> instance that is used to 331: * format the date value 332: */ 333: public SimpleDateFormat getFormat() 334: { 335: return dateFormat; 336: } 337: 338: /** 339: * Returns the {@link SpinnerDateModel} that is edited by this editor. 340: * 341: * @return the <code>SpinnerDateModel</code> that is edited by this editor 342: */ 343: public SpinnerDateModel getModel() 344: { 345: return (SpinnerDateModel) getSpinner().getModel(); 346: } 347: } 348: 349: private static final long serialVersionUID = 3412663575706551720L; 350: 351: /** DOCUMENT ME! */ 352: private SpinnerModel model; 353: 354: /** DOCUMENT ME! */ 355: private JComponent editor; 356: 357: /** DOCUMENT ME! */ 358: private ChangeListener listener = new ChangeListener() 359: { 360: public void stateChanged(ChangeEvent evt) 361: { 362: fireStateChanged(); 363: } 364: }; 365: 366: /** 367: * Creates a JSpinner with <code>SpinnerNumberModel</code> 368: * 369: * @see javax.swing.SpinnerNumberModel 370: */ 371: public JSpinner() 372: { 373: this(new SpinnerNumberModel()); 374: } 375: 376: /** 377: * Creates a JSpinner with the specific model and sets the default editor 378: * 379: * @param model DOCUMENT ME! 380: */ 381: public JSpinner(SpinnerModel model) 382: { 383: this.model = model; 384: model.addChangeListener(listener); 385: setEditor(createEditor(model)); 386: updateUI(); 387: } 388: 389: /** 390: * If the editor is <code>JSpinner.DefaultEditor</code>, then forwards the 391: * call to it, otherwise do nothing. 392: * 393: * @throws ParseException DOCUMENT ME! 394: */ 395: public void commitEdit() throws ParseException 396: { 397: if (editor instanceof DefaultEditor) 398: ((DefaultEditor) editor).commitEdit(); 399: } 400: 401: /** 402: * Gets the current editor 403: * 404: * @return the current editor 405: * 406: * @see #setEditor 407: */ 408: public JComponent getEditor() 409: { 410: return editor; 411: } 412: 413: /** 414: * Changes the current editor to the new editor. This methods should remove 415: * the old listeners (if any) and adds the new listeners (if any). 416: * 417: * @param editor the new editor 418: * 419: * @throws IllegalArgumentException DOCUMENT ME! 420: * 421: * @see #getEditor 422: */ 423: public void setEditor(JComponent editor) 424: { 425: if (editor == null) 426: throw new IllegalArgumentException("editor may not be null"); 427: 428: if (this.editor instanceof DefaultEditor) 429: ((DefaultEditor) editor).dismiss(this); 430: else if (this.editor instanceof ChangeListener) 431: removeChangeListener((ChangeListener) this.editor); 432: 433: if (editor instanceof ChangeListener) 434: addChangeListener((ChangeListener) editor); 435: 436: this.editor = editor; 437: } 438: 439: /** 440: * Gets the underly model. 441: * 442: * @return the underly model 443: */ 444: public SpinnerModel getModel() 445: { 446: return model; 447: } 448: 449: /** 450: * Sets a new underlying model. 451: * 452: * @param newModel the new model to set 453: * 454: * @exception IllegalArgumentException if newModel is <code>null</code> 455: */ 456: public void setModel(SpinnerModel newModel) 457: { 458: if (newModel == null) 459: throw new IllegalArgumentException(); 460: 461: if (model == newModel) 462: return; 463: 464: SpinnerModel oldModel = model; 465: model = newModel; 466: firePropertyChange("model", oldModel, newModel); 467: 468: if (editor == null) 469: setEditor(createEditor(model)); 470: } 471: 472: /** 473: * Gets the next value without changing the current value. 474: * 475: * @return the next value 476: * 477: * @see javax.swing.SpinnerModel#getNextValue 478: */ 479: public Object getNextValue() 480: { 481: return model.getNextValue(); 482: } 483: 484: /** 485: * Gets the previous value without changing the current value. 486: * 487: * @return the previous value 488: * 489: * @see javax.swing.SpinnerModel#getPreviousValue 490: */ 491: public Object getPreviousValue() 492: { 493: return model.getPreviousValue(); 494: } 495: 496: /** 497: * Gets the <code>SpinnerUI</code> that handles this spinner 498: * 499: * @return the <code>SpinnerUI</code> 500: */ 501: public SpinnerUI getUI() 502: { 503: return (SpinnerUI) ui; 504: } 505: 506: /** 507: * Gets the current value of the spinner, according to the underly model, 508: * not the UI. 509: * 510: * @return the current value 511: * 512: * @see javax.swing.SpinnerModel#getValue 513: */ 514: public Object getValue() 515: { 516: return model.getValue(); 517: } 518: 519: /** 520: * DOCUMENT ME! 521: * 522: * @param value DOCUMENT ME! 523: */ 524: public void setValue(Object value) 525: { 526: model.setValue(value); 527: } 528: 529: /** 530: * This method returns a name to identify which look and feel class will be 531: * the UI delegate for this spinner. 532: * 533: * @return The UIClass identifier. "SpinnerUI" 534: */ 535: public String getUIClassID() 536: { 537: return "SpinnerUI"; 538: } 539: 540: /** 541: * This method resets the spinner's UI delegate to the default UI for the 542: * current look and feel. 543: */ 544: public void updateUI() 545: { 546: setUI((SpinnerUI) UIManager.getUI(this)); 547: } 548: 549: /** 550: * This method sets the spinner's UI delegate. 551: * 552: * @param ui The spinner's UI delegate. 553: */ 554: public void setUI(SpinnerUI ui) 555: { 556: super.setUI(ui); 557: } 558: 559: /** 560: * Adds a <code>ChangeListener</code> 561: * 562: * @param listener the listener to add 563: */ 564: public void addChangeListener(ChangeListener listener) 565: { 566: listenerList.add(ChangeListener.class, listener); 567: } 568: 569: /** 570: * Remove a particular listener 571: * 572: * @param listener the listener to remove 573: */ 574: public void removeChangeListener(ChangeListener listener) 575: { 576: listenerList.remove(ChangeListener.class, listener); 577: } 578: 579: /** 580: * Gets all the <code>ChangeListener</code>s 581: * 582: * @return all the <code>ChangeListener</code>s 583: */ 584: public ChangeListener[] getChangeListeners() 585: { 586: return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); 587: } 588: 589: /** 590: * Fires a <code>ChangeEvent</code> to all the <code>ChangeListener</code>s 591: * added to this <code>JSpinner</code> 592: */ 593: protected void fireStateChanged() 594: { 595: ChangeEvent evt = new ChangeEvent(this); 596: ChangeListener[] listeners = getChangeListeners(); 597: 598: for (int i = 0; i < listeners.length; ++i) 599: listeners[i].stateChanged(evt); 600: } 601: 602: /** 603: * Creates an editor for this <code>JSpinner</code>. Really, it should be a 604: * <code>JSpinner.DefaultEditor</code>, but since that should be 605: * implemented by a JFormattedTextField, and one is not written, I am just 606: * using a dummy one backed by a JLabel. 607: * 608: * @param model DOCUMENT ME! 609: * 610: * @return the default editor 611: */ 612: protected JComponent createEditor(SpinnerModel model) 613: { 614: if (model instanceof SpinnerDateModel) 615: return new DateEditor(this); 616: else if (model instanceof SpinnerNumberModel) 617: return new NumberEditor(this); 618: else 619: return new DefaultEditor(this); 620: } 621: }
GNU Classpath (0.18) |