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