001/* 002 * $Id: MemoryOption.java,v 1.8 2011/03/24 16:06:34 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031package edu.wisc.ssec.mcidasv.startupmanager.options; 032 033import java.awt.Color; 034import java.awt.event.ActionEvent; 035import java.awt.event.ActionListener; 036import java.awt.event.KeyAdapter; 037import java.awt.event.KeyEvent; 038import java.util.regex.Matcher; 039import java.util.regex.Pattern; 040 041import javax.swing.ButtonGroup; 042import javax.swing.JComboBox; 043import javax.swing.JComponent; 044import javax.swing.JLabel; 045import javax.swing.JPanel; 046import javax.swing.JRadioButton; 047import javax.swing.JSlider; 048import javax.swing.JTextField; 049import javax.swing.event.ChangeEvent; 050import javax.swing.event.ChangeListener; 051 052import ucar.unidata.util.GuiUtils; 053 054import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 055import edu.wisc.ssec.mcidasv.util.McVTextField; 056import edu.wisc.ssec.mcidasv.startupmanager.StartupManager; 057import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster.OptionPlatform; 058import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster.Type; 059import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster.Visibility; 060 061public class MemoryOption extends AbstractOption implements ActionListener { 062 public enum Prefix { 063 MEGA("M", "megabytes"), 064 GIGA("G", "gigabytes"), 065 TERA("T", "terabytes"), 066 PERCENT("P", "percent"); 067 068 private final String javaChar; 069 private final String name; 070 071 private Prefix(final String javaChar, final String name) { 072 this.javaChar = javaChar; 073 this.name = name; 074 } 075 076 public String getJavaChar() { 077 return javaChar.toUpperCase(); 078 } 079 080 public String getName() { 081 return name; 082 } 083 084 public String getJavaFormat(final String value) { 085 long longVal = Long.parseLong(value); 086 return longVal + javaChar; 087 } 088 089 @Override public String toString() { 090 return name; 091 } 092 } 093 094 private enum State { 095 VALID(Color.BLACK, Color.WHITE), 096 WARN(Color.BLACK, new Color(255, 255, 204)), 097 ERROR(Color.WHITE, Color.PINK); 098 099 private final Color foreground; 100 private final Color background; 101 102 private State(final Color foreground, final Color background) { 103 this.foreground = foreground; 104 this.background = background; 105 } 106 107 public Color getForeground() { return foreground; } 108 public Color getBackground() { return background; } 109 } 110 111 private final static Prefix[] PREFIXES = { Prefix.MEGA, Prefix.GIGA, Prefix.TERA }; 112 private Prefix currentPrefix = Prefix.MEGA; 113 114 private static final Pattern MEMSTRING = 115 Pattern.compile("^(\\d+)([M|G|T|P]?)$", Pattern.CASE_INSENSITIVE); 116 117 private final String defaultPrefValue; 118 private String failsafeValue = "512M"; 119 private String value = failsafeValue; // bootstrap 120 121 private JRadioButton jrbSlider = new JRadioButton(); 122 private JRadioButton jrbNumber = new JRadioButton(); 123 private ButtonGroup jtbBg = GuiUtils.buttonGroup(jrbSlider, jrbNumber); 124 125 private JPanel sliderPanel = new JPanel(); 126 private JLabel sliderLabel = new JLabel(); 127 private JSlider slider = new JSlider(); 128 129 private JPanel textPanel = new JPanel(); 130 private McVTextField text = new McVTextField(); 131 private JComboBox memVals = new JComboBox(PREFIXES); 132 private String initTextValue = value; 133 private Prefix initPrefixValue = currentPrefix; 134 135 private int minSliderValue = 10; 136 private int maxSliderValue = 80; 137 private int initSliderValue = minSliderValue; 138 139 private int maxmem = StartupManager.getMaximumHeapSize(); 140 141 private State currentState = State.VALID; 142 143 private boolean doneInit = false; 144 145 public MemoryOption(final String id, final String label, 146 final String defaultValue, final OptionPlatform optionPlatform, 147 final Visibility optionVisibility) 148 { 149 super(id, label, Type.MEMORY, optionPlatform, optionVisibility); 150 if (maxmem==0) defaultPrefValue=failsafeValue; 151 else defaultPrefValue = defaultValue; 152 try { 153 setValue(defaultPrefValue); 154 } catch (IllegalArgumentException e) { 155 setValue(value); 156 } 157 text.setAllow(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}); 158 jrbSlider.setActionCommand("slider"); 159 jrbSlider.addActionListener(this); 160 jrbNumber.setActionCommand("number"); 161 jrbNumber.addActionListener(this); 162 sliderPanel.setEnabled(false); 163 textPanel.setEnabled(false); 164 } 165 166 private String[] getNames(final Prefix[] arr) { 167 assert arr != null; 168 String[] newArr = new String[arr.length]; 169 for (int i = 0; i < arr.length; i++) 170 newArr[i] = arr[i].getName(); 171 return newArr; 172 } 173 174 private void setState(final State newState) { 175 assert newState != null : newState; 176 currentState = newState; 177 text.setForeground(currentState.getForeground()); 178 text.setBackground(currentState.getBackground()); 179 } 180 181 private boolean isValid() { 182 return currentState == State.VALID; 183 } 184 185 private boolean isSlider() { 186 return currentPrefix.equals(Prefix.PERCENT); 187 } 188 189 public void actionPerformed(ActionEvent e) { 190 if (e.getActionCommand().equals("slider")) { 191 GuiUtils.enableTree(sliderPanel, true); 192 GuiUtils.enableTree(textPanel, false); 193 // Trigger the listener 194 int sliderValue = slider.getValue(); 195 if (sliderValue==minSliderValue) 196 slider.setValue(maxSliderValue); 197 else 198 slider.setValue(minSliderValue); 199 slider.setValue(sliderValue); 200 } 201 else { 202 GuiUtils.enableTree(sliderPanel, false); 203 GuiUtils.enableTree(textPanel, true); 204 // Trigger the listener 205 handleNewValue(text, memVals); 206 } 207 } 208 209 public ChangeListener percentListener = new ChangeListener() { 210 public void stateChanged(ChangeEvent evt) { 211 if (!sliderPanel.isEnabled()) return; 212 int sliderValue = ((JSlider)evt.getSource()).getValue(); 213 setValue(sliderValue + Prefix.PERCENT.getJavaChar()); 214 text.setText("" + Math.round(sliderValue / 100.0 * maxmem)); 215 } 216 }; 217 218 private void handleNewValue(final JTextField field, final JComboBox box) { 219 if (!textPanel.isEnabled()) return; 220 assert field != null; 221 assert box != null; 222 223 try { 224 String newValue = field.getText(); 225 String huh = ((Prefix)box.getSelectedItem()).getJavaFormat(newValue); 226 227 if (!isValid()) 228 setState(State.VALID); 229 230 setValue(huh); 231 } catch (IllegalArgumentException e) { 232 setState(State.ERROR); 233 text.setToolTipText("This value must be an integer greater than zero."); 234 } 235 } 236 237 public JComponent getComponent() { 238 JPanel topPanel = GuiUtils.hbox(jrbSlider, getSliderComponent()); 239 JPanel bottomPanel = GuiUtils.hbox(jrbNumber, getTextComponent()); 240 if (isSlider()) { 241 GuiUtils.enableTree(sliderPanel, true); 242 GuiUtils.enableTree(textPanel, false); 243 } 244 else { 245 GuiUtils.enableTree(sliderPanel, false); 246 GuiUtils.enableTree(textPanel, true); 247 } 248 if (maxmem==0) { 249 jrbSlider.setEnabled(false); 250// jrbNumber.setEnabled(false); 251 } 252 doneInit = true; 253 return McVGuiUtils.topBottom(topPanel, bottomPanel, null); 254 } 255 256 public JComponent getSliderComponent() { 257 sliderLabel = new JLabel("Use "+initSliderValue+"% "); 258 String memoryString = maxmem + "mb"; 259 if (maxmem==0) memoryString="Unknown"; 260 JLabel postLabel = new JLabel(" of available memory (" + memoryString + ")"); 261 JComponent[] sliderComps = GuiUtils.makeSliderPopup(minSliderValue, maxSliderValue+1, initSliderValue, percentListener); 262 slider = (JSlider) sliderComps[1]; 263 slider.setMinorTickSpacing(5); 264 slider.setMajorTickSpacing(10); 265 slider.setSnapToTicks(true); 266 slider.setExtent(1); 267 slider.setPaintTicks(true); 268 slider.setPaintLabels(true); 269 sliderComps[0].setToolTipText("Set maximum memory by percent"); 270 sliderPanel = GuiUtils.hbox(sliderLabel, sliderComps[0], postLabel); 271 return sliderPanel; 272 } 273 274 public JComponent getTextComponent() { 275 text.setText(initTextValue); 276 text.addKeyListener(new KeyAdapter() { 277 public void keyReleased(final KeyEvent e) { 278 handleNewValue(text, memVals); 279 } 280 }); 281 memVals.setSelectedItem(initPrefixValue); 282 memVals.addActionListener(new ActionListener() { 283 public void actionPerformed(final ActionEvent e) { 284 handleNewValue(text, memVals); 285 } 286 }); 287 McVGuiUtils.setComponentWidth(text, McVGuiUtils.Width.ONEHALF); 288 McVGuiUtils.setComponentWidth(memVals, McVGuiUtils.Width.ONEHALF); 289 textPanel = GuiUtils.hbox(text, memVals); 290 return textPanel; 291 } 292 293 public String toString() { 294 return String.format( 295 "[MemoryOption@%x: value=%s, currentPrefix=%s, isSlider=%s]", 296 hashCode(), value, currentPrefix, isSlider()); 297 } 298 299 public String getValue() { 300 if (!isValid()) 301 return defaultPrefValue; 302 return currentPrefix.getJavaFormat(value); 303 } 304 305 // overridden so that any illegal vals coming *out of* a runMcV.prefs 306 // can be replaced with a legal val. 307 @Override public void fromPrefsFormat(final String prefText) { 308 try { 309 super.fromPrefsFormat(prefText); 310 } catch (IllegalArgumentException e) { 311 setValue(failsafeValue); 312 } 313 } 314 315 public void setValue(final String newValue) { 316 Matcher m = MEMSTRING.matcher(newValue); 317 if (!m.matches()) 318 throw new IllegalArgumentException("Badly formatted memory string: "+newValue); 319 320 String quantity = m.group(1); 321 String prefix = m.group(2); 322 323 // Fall back on failsafe value if user wants a percentage of an unknown maxmem 324 if (maxmem==0 && prefix.toUpperCase().equals(Prefix.PERCENT.getJavaChar())) { 325 m = MEMSTRING.matcher(failsafeValue); 326 if (!m.matches()) 327 throw new IllegalArgumentException("Badly formatted memory string: "+failsafeValue); 328 quantity = m.group(1); 329 prefix = m.group(2); 330 } 331 332 int intVal = Integer.parseInt(quantity); 333 if (intVal <= 0) 334 throw new IllegalArgumentException("Memory cannot be less than or equal to zero: "+newValue); 335 if (prefix.length() == 0) 336 prefix = "M"; 337 338 value = quantity; 339 340 if (prefix.toUpperCase().equals(Prefix.PERCENT.getJavaChar())) { 341 currentPrefix = Prefix.PERCENT; 342 343 // Work around all the default settings going on 344 initSliderValue = Integer.parseInt(value); 345 initPrefixValue = Prefix.MEGA; 346 initTextValue = "" + (int)Math.round(initSliderValue * maxmem / 100.0); 347 348 sliderLabel.setText("Use "+value+"% "); 349 if (maxmem>0) { 350 memVals.setSelectedItem(initPrefixValue); 351 text.setText(initTextValue); 352 } 353 if (!doneInit) jrbSlider.setSelected(true); 354 return; 355 } 356 357 for (Prefix tmp : PREFIXES) { 358 if (prefix.toUpperCase().equals(tmp.getJavaChar())) { 359 currentPrefix = tmp; 360 361 // Work around all the default settings going on 362 initSliderValue = minSliderValue; 363 initPrefixValue = currentPrefix; 364 initTextValue = value; 365 366 if (maxmem>0) { 367 int multiplier = 1; 368 if (currentPrefix.equals(Prefix.GIGA)) multiplier=1024; 369 else if (currentPrefix.equals(Prefix.TERA)) multiplier=1024 * 1024; 370 initSliderValue = (int)Math.round(Integer.parseInt(value) * 100.0 * multiplier / maxmem); 371 initSliderValue = Math.max(Math.min(initSliderValue, maxSliderValue), minSliderValue); 372 slider.setValue(initSliderValue); 373 sliderLabel.setText("Use "+initSliderValue+"% "); 374 } 375 if (!doneInit) jrbNumber.setSelected(true); 376 return; 377 } 378 } 379 380 throw new IllegalArgumentException("Could not find matching memory prefix for \""+prefix+"\" in string: "+newValue); 381 } 382}