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