001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas/ 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see https://www.gnu.org/licenses/. 027 */ 028 029package edu.wisc.ssec.mcidasv.data.dateChooser; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.Component; 034import java.awt.Dimension; 035import java.awt.Font; 036import java.awt.event.ActionEvent; 037import java.awt.event.ActionListener; 038import java.awt.event.FocusEvent; 039import java.awt.event.FocusListener; 040 041import javax.swing.BorderFactory; 042import javax.swing.JFrame; 043import javax.swing.JPanel; 044import javax.swing.JSpinner; 045import javax.swing.JTextField; 046import javax.swing.SpinnerNumberModel; 047import javax.swing.SwingConstants; 048import javax.swing.UIManager; 049import javax.swing.event.CaretEvent; 050import javax.swing.event.CaretListener; 051import javax.swing.event.ChangeEvent; 052import javax.swing.event.ChangeListener; 053 054/** 055 * JSpinField is a numeric field with 2 spin buttons to increase or decrease the 056 * value. It has the same interface as the "old" JSpinField but uses a JSpinner 057 * internally (since J2SE SDK 1.4) rather than a scrollbar for emulating the 058 * spin buttons. 059 * 060 * @author Kai Toedter 061 * @version $LastChangedRevision: 85 $ 062 * @version $LastChangedDate: 2006-04-28 13:50:52 +0200 (Fr, 28 Apr 2006) $ 063 */ 064public class JSpinField extends JPanel implements ChangeListener, CaretListener, ActionListener, 065 FocusListener { 066 private static final long serialVersionUID = 1694904792717740650L; 067 068 protected JSpinner spinner; 069 070 /** the text (number) field */ 071 protected JTextField textField; 072 protected int min; 073 protected int max; 074 protected int value; 075 protected Color darkGreen; 076 077 /** 078 * Default JSpinField constructor. The valid value range is between 079 * Integer.MIN_VALUE and Integer.MAX_VALUE. The initial value is 0. 080 */ 081 public JSpinField() { 082 this(Integer.MIN_VALUE, Integer.MAX_VALUE); 083 } 084 085 /** 086 * JSpinField constructor with given minimum and maximum vaues and initial 087 * value 0. 088 */ 089 public JSpinField(int min, int max) { 090 super(); 091 setName("JSpinField"); 092 this.min = min; 093 if (max < min) 094 max = min; 095 this.max = max; 096 value = 0; 097 if (value < min) 098 value = min; 099 if (value > max) 100 value = max; 101 102 darkGreen = new Color(0, 150, 0); 103 setLayout(new BorderLayout()); 104 textField = new JTextField(); 105 textField.addCaretListener(this); 106 textField.addActionListener(this); 107 textField.setHorizontalAlignment(SwingConstants.RIGHT); 108 textField.setBorder(BorderFactory.createEmptyBorder()); 109 textField.setText(Integer.toString(value)); 110 textField.addFocusListener(this); 111 spinner = new JSpinner() { 112 private static final long serialVersionUID = -6287709243342021172L; 113 private JTextField textField = new JTextField(); 114 115 public Dimension getPreferredSize() { 116 Dimension size = super.getPreferredSize(); 117 return new Dimension(size.width, textField.getPreferredSize().height); 118 } 119 }; 120 spinner.setEditor(textField); 121 spinner.addChangeListener(this); 122 // spinner.setSize(spinner.getWidth(), textField.getHeight()); 123 add(spinner, BorderLayout.CENTER); 124 } 125 126 public void adjustWidthToMaximumValue() { 127 JTextField testTextField = new JTextField(Integer.toString(max)); 128 int width = testTextField.getPreferredSize().width; 129 int height = testTextField.getPreferredSize().height; 130 textField.setPreferredSize(new Dimension(width, height)); 131 textField.revalidate(); 132 } 133 134 /** 135 * Is invoked when the spinner model changes 136 * 137 * @param e 138 * the ChangeEvent 139 */ 140 public void stateChanged(ChangeEvent e) { 141 SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel(); 142 int value = model.getNumber().intValue(); 143 setValue(value); 144 } 145 146 /** 147 * Sets the value attribute of the JSpinField object. 148 * 149 * @param newValue 150 * The new value 151 * @param updateTextField 152 * true if text field should be updated 153 */ 154 protected void setValue(int newValue, boolean updateTextField, boolean firePropertyChange) { 155 int oldValue = value; 156 if (newValue < min) { 157 value = min; 158 } else if (newValue > max) { 159 value = max; 160 } else { 161 value = newValue; 162 } 163 164 if (updateTextField) { 165 textField.setText(Integer.toString(value)); 166 textField.setForeground(Color.black); 167 } 168 169 if (firePropertyChange) { 170 firePropertyChange("value", oldValue, value); 171 } 172 } 173 174 /** 175 * Sets the value. This is a bound property. 176 * 177 * @param newValue 178 * the new value 179 * 180 * @see #getValue 181 */ 182 public void setValue(int newValue) { 183 setValue(newValue, true, true); 184 spinner.setValue(Integer.valueOf(value)); 185 } 186 187 /** 188 * Returns the value. 189 * 190 * @return the value value 191 */ 192 public int getValue() { 193 return value; 194 } 195 196 /** 197 * Sets the minimum value. 198 * 199 * @param newMinimum 200 * the new minimum value 201 * 202 * @see #getMinimum 203 */ 204 public void setMinimum(int newMinimum) { 205 min = newMinimum; 206 } 207 208 /** 209 * Returns the minimum value. 210 * 211 * @return the minimum value 212 */ 213 public int getMinimum() { 214 return min; 215 } 216 217 /** 218 * Sets the maximum value and adjusts the preferred width. 219 * 220 * @param newMaximum 221 * the new maximum value 222 * 223 * @see #getMaximum 224 */ 225 public void setMaximum(int newMaximum) { 226 max = newMaximum; 227 } 228 229 /** 230 * Sets the horizontal alignment of the displayed value. 231 * 232 * @param alignment 233 * the horizontal alignment 234 */ 235 public void setHorizontalAlignment(int alignment) { 236 textField.setHorizontalAlignment(alignment); 237 } 238 239 /** 240 * Returns the maximum value. 241 * 242 * @return the maximum value 243 */ 244 public int getMaximum() { 245 return max; 246 } 247 248 /** 249 * Sets the font property. 250 * 251 * @param font 252 * the new font 253 */ 254 public void setFont(Font font) { 255 if (textField != null) { 256 textField.setFont(font); 257 } 258 } 259 260 /** 261 * Sets the foreground 262 * 263 * @param fg 264 * the foreground 265 */ 266 public void setForeground(Color fg) { 267 if (textField != null) { 268 textField.setForeground(fg); 269 } 270 } 271 272 /** 273 * After any user input, the value of the textfield is proofed. Depending on 274 * being an integer, the value is colored green or red. 275 * 276 * @param e 277 * the caret event 278 */ 279 public void caretUpdate(CaretEvent e) { 280 try { 281 int testValue = Integer.valueOf(textField.getText()).intValue(); 282 283 if ((testValue >= min) && (testValue <= max)) { 284 textField.setForeground(darkGreen); 285 setValue(testValue, false, true); 286 } else { 287 textField.setForeground(Color.red); 288 } 289 } catch (Exception ex) { 290 if (ex instanceof NumberFormatException) { 291 textField.setForeground(Color.red); 292 } 293 294 // Ignore all other exceptions, e.g. illegal state exception 295 } 296 297 textField.repaint(); 298 } 299 300 /** 301 * After any user input, the value of the textfield is proofed. Depending on 302 * being an integer, the value is colored green or red. If the textfield is 303 * green, the enter key is accepted and the new value is set. 304 * 305 * @param e 306 * Description of the Parameter 307 */ 308 public void actionPerformed(ActionEvent e) { 309 if (textField.getForeground().equals(darkGreen)) { 310 setValue(Integer.valueOf(textField.getText()).intValue()); 311 } 312 } 313 314 /** 315 * Enable or disable the JSpinField. 316 * 317 * @param enabled 318 * The new enabled value 319 */ 320 public void setEnabled(boolean enabled) { 321 super.setEnabled(enabled); 322 spinner.setEnabled(enabled); 323 textField.setEnabled(enabled); 324 /* 325 * Fixes the background bug 326 * 4991597 and sets the background explicitely to a 327 * TextField.inactiveBackground. 328 */ 329 if (!enabled) { 330 textField.setBackground(UIManager.getColor("TextField.inactiveBackground")); 331 } 332 } 333 334 /** 335 * Returns the year chooser's spinner (which allow the focus to be set to 336 * it). 337 * 338 * @return Component the spinner or null, if the month chooser has no 339 * spinner 340 */ 341 public Component getSpinner() { 342 return spinner; 343 } 344 345 /** 346 * Creates a JFrame with a JSpinField inside and can be used for testing. 347 * 348 * @param s 349 * The command line arguments 350 */ 351 public static void main(String[] s) { 352 JFrame frame = new JFrame("JSpinField"); 353 frame.getContentPane().add(new JSpinField()); 354 frame.pack(); 355 frame.setVisible(true); 356 } 357 358 /* 359 * (non-Javadoc) 360 * 361 * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) 362 */ 363 public void focusGained(FocusEvent e) { 364 } 365 366 /** 367 * The value of the text field is checked against a valid (green) value. If 368 * valid, the value is set and a property change is fired. 369 */ 370 public void focusLost(FocusEvent e) { 371 actionPerformed(null); 372 } 373}