GNU Classpath (0.20) | |
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() throws ParseException 112: { 113: // TODO: Implement this properly. 114: } 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: Implement this properly. 188: } 189: 190: /** 191: * DOCUMENT ME! 192: * 193: * @param event DOCUMENT ME! 194: */ 195: public void stateChanged(ChangeEvent event) 196: { 197: // TODO: Implement this properly. 198: } 199: 200: public void removeLayoutComponent(Component child) 201: { 202: // Nothing to do here. 203: } 204: 205: /** 206: * DOCUMENT ME! 207: * 208: * @param name DOCUMENT ME! 209: * @param child DOCUMENT ME! 210: */ 211: public void addLayoutComponent(String name, Component child) 212: { 213: // Nothing to do here. 214: } 215: } 216: 217: /** 218: * DOCUMENT ME! 219: */ 220: public static class NumberEditor extends DefaultEditor 221: { 222: /** 223: * For compatability with Sun's JDK 224: */ 225: private static final long serialVersionUID = 3791956183098282942L; 226: 227: /** 228: * Creates a new NumberEditor object. 229: * 230: * @param spinner DOCUMENT ME! 231: */ 232: public NumberEditor(JSpinner spinner) 233: { 234: super(spinner); 235: } 236: 237: /** 238: * Creates a new NumberEditor object. 239: * 240: * @param spinner DOCUMENT ME! 241: */ 242: public NumberEditor(JSpinner spinner, String decimalFormatPattern) 243: { 244: super(spinner); 245: } 246: 247: /** 248: * DOCUMENT ME! 249: * 250: * @return DOCUMENT ME! 251: */ 252: public DecimalFormat getFormat() 253: { 254: return null; 255: } 256: 257: public SpinnerNumberModel getModel() 258: { 259: return (SpinnerNumberModel) getSpinner().getModel(); 260: } 261: } 262: 263: /** 264: * A <code>JSpinner</code> editor used for the {@link SpinnerListModel}. 265: * This editor uses a <code>JFormattedTextField</code> to edit the values 266: * of the spinner. 267: * 268: * @author Roman Kennke (kennke@aicas.com) 269: */ 270: public static class ListEditor extends DefaultEditor 271: { 272: /** 273: * Creates a new instance of <code>ListEditor</code>. 274: * 275: * @param spinner the spinner for which this editor is used 276: */ 277: public ListEditor(JSpinner spinner) 278: { 279: super(spinner); 280: } 281: 282: public SpinnerListModel getModel() 283: { 284: return (SpinnerListModel) getSpinner().getModel(); 285: } 286: } 287: 288: /** 289: * An editor class for a <code>JSpinner</code> that is used 290: * for displaying and editing dates (e.g. that uses 291: * <code>SpinnerDateModel</code> as model). 292: * 293: * The editor uses a {@link JTextField} with the value 294: * displayed by a {@link DateFormatter} instance. 295: */ 296: public static class DateEditor extends DefaultEditor 297: { 298: 299: /** The serialVersionUID. */ 300: private static final long serialVersionUID = -4279356973770397815L; 301: 302: /** The DateFormat instance used to format the date. */ 303: SimpleDateFormat dateFormat; 304: 305: /** 306: * Creates a new instance of DateEditor for the specified 307: * <code>JSpinner</code>. 308: * 309: * @param spinner the <code>JSpinner</code> for which to 310: * create a <code>DateEditor</code> instance 311: */ 312: public DateEditor(JSpinner spinner) 313: { 314: super(spinner); 315: init(new SimpleDateFormat()); 316: } 317: 318: /** 319: * Creates a new instance of DateEditor for the specified 320: * <code>JSpinner</code> using the specified date format 321: * pattern. 322: * 323: * @param spinner the <code>JSpinner</code> for which to 324: * create a <code>DateEditor</code> instance 325: * @param dateFormatPattern the date format to use 326: * 327: * @see SimpleDateFormat#SimpleDateFormat(String) 328: */ 329: public DateEditor(JSpinner spinner, String dateFormatPattern) 330: { 331: super(spinner); 332: init(new SimpleDateFormat(dateFormatPattern)); 333: } 334: 335: /** 336: * Initializes the JFormattedTextField for this editor. 337: * 338: * @param format the date format to use in the formatted text field 339: */ 340: private void init(SimpleDateFormat format) 341: { 342: dateFormat = format; 343: getTextField().setFormatterFactory( 344: new JFormattedTextField.AbstractFormatterFactory() 345: { 346: public JFormattedTextField.AbstractFormatter 347: getFormatter(JFormattedTextField ftf) 348: { 349: return new DateFormatter(dateFormat); 350: } 351: }); 352: } 353: 354: /** 355: * Returns the <code>SimpleDateFormat</code> instance that is used to 356: * format the date value. 357: * 358: * @return the <code>SimpleDateFormat</code> instance that is used to 359: * format the date value 360: */ 361: public SimpleDateFormat getFormat() 362: { 363: return dateFormat; 364: } 365: 366: /** 367: * Returns the {@link SpinnerDateModel} that is edited by this editor. 368: * 369: * @return the <code>SpinnerDateModel</code> that is edited by this editor 370: */ 371: public SpinnerDateModel getModel() 372: { 373: return (SpinnerDateModel) getSpinner().getModel(); 374: } 375: } 376: 377: private static final long serialVersionUID = 3412663575706551720L; 378: 379: /** DOCUMENT ME! */ 380: private SpinnerModel model; 381: 382: /** DOCUMENT ME! */ 383: private JComponent editor; 384: 385: /** DOCUMENT ME! */ 386: private ChangeListener listener = new ChangeListener() 387: { 388: public void stateChanged(ChangeEvent evt) 389: { 390: fireStateChanged(); 391: } 392: }; 393: 394: /** 395: * Creates a JSpinner with <code>SpinnerNumberModel</code> 396: * 397: * @see javax.swing.SpinnerNumberModel 398: */ 399: public JSpinner() 400: { 401: this(new SpinnerNumberModel()); 402: } 403: 404: /** 405: * Creates a JSpinner with the specific model and sets the default editor 406: * 407: * @param model DOCUMENT ME! 408: */ 409: public JSpinner(SpinnerModel model) 410: { 411: this.model = model; 412: model.addChangeListener(listener); 413: setEditor(createEditor(model)); 414: updateUI(); 415: } 416: 417: /** 418: * If the editor is <code>JSpinner.DefaultEditor</code>, then forwards the 419: * call to it, otherwise do nothing. 420: * 421: * @throws ParseException DOCUMENT ME! 422: */ 423: public void commitEdit() throws ParseException 424: { 425: if (editor instanceof DefaultEditor) 426: ((DefaultEditor) editor).commitEdit(); 427: } 428: 429: /** 430: * Gets the current editor 431: * 432: * @return the current editor 433: * 434: * @see #setEditor 435: */ 436: public JComponent getEditor() 437: { 438: return editor; 439: } 440: 441: /** 442: * Changes the current editor to the new editor. This methods should remove 443: * the old listeners (if any) and adds the new listeners (if any). 444: * 445: * @param editor the new editor 446: * 447: * @throws IllegalArgumentException DOCUMENT ME! 448: * 449: * @see #getEditor 450: */ 451: public void setEditor(JComponent editor) 452: { 453: if (editor == null) 454: throw new IllegalArgumentException("editor may not be null"); 455: 456: if (this.editor instanceof DefaultEditor) 457: ((DefaultEditor) editor).dismiss(this); 458: else if (this.editor instanceof ChangeListener) 459: removeChangeListener((ChangeListener) this.editor); 460: 461: if (editor instanceof ChangeListener) 462: addChangeListener((ChangeListener) editor); 463: 464: this.editor = editor; 465: } 466: 467: /** 468: * Gets the underly model. 469: * 470: * @return the underly model 471: */ 472: public SpinnerModel getModel() 473: { 474: return model; 475: } 476: 477: /** 478: * Sets a new underlying model. 479: * 480: * @param newModel the new model to set 481: * 482: * @exception IllegalArgumentException if newModel is <code>null</code> 483: */ 484: public void setModel(SpinnerModel newModel) 485: { 486: if (newModel == null) 487: throw new IllegalArgumentException(); 488: 489: if (model == newModel) 490: return; 491: 492: SpinnerModel oldModel = model; 493: model = newModel; 494: firePropertyChange("model", oldModel, newModel); 495: 496: if (editor == null) 497: setEditor(createEditor(model)); 498: } 499: 500: /** 501: * Gets the next value without changing the current value. 502: * 503: * @return the next value 504: * 505: * @see javax.swing.SpinnerModel#getNextValue 506: */ 507: public Object getNextValue() 508: { 509: return model.getNextValue(); 510: } 511: 512: /** 513: * Gets the previous value without changing the current value. 514: * 515: * @return the previous value 516: * 517: * @see javax.swing.SpinnerModel#getPreviousValue 518: */ 519: public Object getPreviousValue() 520: { 521: return model.getPreviousValue(); 522: } 523: 524: /** 525: * Gets the <code>SpinnerUI</code> that handles this spinner 526: * 527: * @return the <code>SpinnerUI</code> 528: */ 529: public SpinnerUI getUI() 530: { 531: return (SpinnerUI) ui; 532: } 533: 534: /** 535: * Gets the current value of the spinner, according to the underly model, 536: * not the UI. 537: * 538: * @return the current value 539: * 540: * @see javax.swing.SpinnerModel#getValue 541: */ 542: public Object getValue() 543: { 544: return model.getValue(); 545: } 546: 547: /** 548: * DOCUMENT ME! 549: * 550: * @param value DOCUMENT ME! 551: */ 552: public void setValue(Object value) 553: { 554: model.setValue(value); 555: } 556: 557: /** 558: * This method returns a name to identify which look and feel class will be 559: * the UI delegate for this spinner. 560: * 561: * @return The UIClass identifier. "SpinnerUI" 562: */ 563: public String getUIClassID() 564: { 565: return "SpinnerUI"; 566: } 567: 568: /** 569: * This method resets the spinner's UI delegate to the default UI for the 570: * current look and feel. 571: */ 572: public void updateUI() 573: { 574: setUI((SpinnerUI) UIManager.getUI(this)); 575: } 576: 577: /** 578: * This method sets the spinner's UI delegate. 579: * 580: * @param ui The spinner's UI delegate. 581: */ 582: public void setUI(SpinnerUI ui) 583: { 584: super.setUI(ui); 585: } 586: 587: /** 588: * Adds a <code>ChangeListener</code> 589: * 590: * @param listener the listener to add 591: */ 592: public void addChangeListener(ChangeListener listener) 593: { 594: listenerList.add(ChangeListener.class, listener); 595: } 596: 597: /** 598: * Remove a particular listener 599: * 600: * @param listener the listener to remove 601: */ 602: public void removeChangeListener(ChangeListener listener) 603: { 604: listenerList.remove(ChangeListener.class, listener); 605: } 606: 607: /** 608: * Gets all the <code>ChangeListener</code>s 609: * 610: * @return all the <code>ChangeListener</code>s 611: */ 612: public ChangeListener[] getChangeListeners() 613: { 614: return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); 615: } 616: 617: /** 618: * Fires a <code>ChangeEvent</code> to all the <code>ChangeListener</code>s 619: * added to this <code>JSpinner</code> 620: */ 621: protected void fireStateChanged() 622: { 623: ChangeEvent evt = new ChangeEvent(this); 624: ChangeListener[] listeners = getChangeListeners(); 625: 626: for (int i = 0; i < listeners.length; ++i) 627: listeners[i].stateChanged(evt); 628: } 629: 630: /** 631: * Creates an editor for this <code>JSpinner</code>. Really, it should be a 632: * <code>JSpinner.DefaultEditor</code>, but since that should be 633: * implemented by a JFormattedTextField, and one is not written, I am just 634: * using a dummy one backed by a JLabel. 635: * 636: * @param model DOCUMENT ME! 637: * 638: * @return the default editor 639: */ 640: protected JComponent createEditor(SpinnerModel model) 641: { 642: if (model instanceof SpinnerDateModel) 643: return new DateEditor(this); 644: else if (model instanceof SpinnerNumberModel) 645: return new NumberEditor(this); 646: else 647: return new DefaultEditor(this); 648: } 649: }
GNU Classpath (0.20) |